Smalltalk

Quick Links

Accessors Add Method Benchmarks Binary Messages Blocks Browse/Do/Inspect/Print It Browse or Edit Method Characters Classes Code Formatting Comments Configuration Constants Create Class Create Category Create New Object Debugger Delete Class Delete Method Enumerated Types Exception Handling FFI Filein/Fileout Find Class File List Flaps Fonts Images Implementers Implementations Indentation Inheritance Inspector Interfaces Introduction Keyboard Input Keyboard Shortcuts Literal/Dynamic Arrays LogicalExpressions MessagePrecedence Messages Method Arguments Method Finder Methods Morphic Monticello Mouse Buttons Naming Conventions OmniBrowser Other Types Packages Persistence Projects Reasons Disliked Reasons Liked Refactoring Reflection Regular Expressions ReservedKeywords Resources Running Headless Seaside Senders Squeak Environment Starting and Quitting Startup and Shutdown Statements Stream I/O Strings SVI Symbols System Browser Transcript Unit Tests Variable References Variables Web Applications Windows Workspace World Menu

Introduction

"Any object-oriented language, given enough time, will become a poor reinvention of Smalltalk. "
- Robert Fischer
Smalltalk Byte cover
"Actually I made up the term "object-oriented" and I can tell you I did not have C++ in mind."
- Alan Kay

The following was adapted from http://www.squeak.org/Smalltalk/.

Resources

Reasons some people like Smalltalk

  1. It has a small, consistently applied syntax.
  2. It has a great development environment consisting of tools such as System Browser, Workspace, Transcript, Debugger, Hierarchy Browser, Method Finder and more
  3. Everything is an object.
  4. It provides automatic version control.
  5. It provides extreme polymorphism. Any kind of object can be passed to a method as long as it responds to the messages that will be sent to it.
  6. It has a great web app. framework (Seaside) and a great CMS framework (Pier).

Reasons some people dislike Smalltalk

  1. It isn't as popular as many other programming languages.
  2. It doesn't minimize compile-time errors as much as statically typed languages such as C++, C# and Java. However, it does do incremental compiling when methods are saved so it finds syntax errors before runtime, unlike most scripting languages.
  3. All the code for a project is stored in one big image file (often over 30 MB).
  4. The syntax is fairly different from most programming languages.
  5. Performance may be an issue. However, VisualWorks Smalltalk is almost twice as fast as Python which is faster than Perl which is faster than Ruby.
  6. Classes are not in a namespace, so all class names must be unique. Using class name prefixes is recommended. This is important for using Squeak packages and Monticello. Squeak has a prefix registry in the wiki. Note that Cincom Smalltalk does support namespaces.

Implementations

This is a partial list of open source implementations.

Concepts

Images

The Smalltalk environment is stored in an "image" file. It contains:

Changes can be saved in the current image or in a new image file.

Classes

Classes are templates for creating objects. They define class variables, instance variables, class methods, instance methods, and more. They are organized into categories (similar to packages in Java) to make them easier to find, but their names must be unique across all categories. They are described by Class objects that are in the Kernel-Class category. In Squeak Smalltalk the Class class is a subclass of Behavior.

The concepts of abstract classes and methods are not directly supported in Smalltalk. A method is essentially abstract if it returns "self subclassResponsibility". A class is essentially abstract if it contains any abstract methods.

To get an Array containing all the current instances of a given class, send allInstances to the class. For example, instances := SystemWindow allInstances.

Inheritance

Smalltalk supports single inheritace, not multiple. The superclass of every class is specified in its definition and is often simply Object. For example, Object subclass: #FooBar.

In Squeak Smalltalk, the Object class is a subclass of ProtoObject.

Interfaces

Smalltalk doesn't support the concept of interfaces. Instead it relies on an extreme form of polymorphism. Any message can be sent to any object as the object responds to it. This makes creating mock objects really easy. If the object doesn't respond to the message then the method doesNotUnderstand: will be invoked with the message name symbol as an argument. This is defined in the class Object which signals a MessageNotUnderstood error. The doesNotUnderstand method can be overridden to do interesting metaprogramming things.

doesNotUnderstand: aMessage
  "handle unrecognized messages"
  Transcript show: 'I don''t recognize "', aMessage asString, '".'

Characters

Values representing a single character are specified with a $ followed by the character. For example, the plus character is represented by $+. The Character class has class methods that return whitespace characters such as space and tab.

Strings

Literal strings are surrounded by single quotes. This is convenient because it seems that double quotes are needed inside string values more often than single quotes. To embed a single quote, use two consecutive single quotes. To concatenate strings, list any number of literal strings and/or string variables with commas in between. For example, m := 'Mark'. f := m, 'Tami'. f is now 'MarkTami'. To concatenate non-string values to strings, call asString on them. For example, "n := 3. s := n printString, 'D'.".

An alternative to converting non-String objects to Strings for the purpose of concatenation is to use the String format: method whose parameter is a Collection. The previous example could be written as follows.

n := 3.
s := '3{1}' format: { n }

The receiver String can contain any number of indexed placeholders represented by an integer inside curly braces. The parameter is often created using a dynamic array.

To append one String to another, a new String must be created. For example, "s1 := s1, s2". If many appends must be performed, consider using a WriteStream as follows, which is much faster:

s := String streamContents: [:stream |
  stream
    nextPutAll: 'foo';
    nextPutAll: 'bar'
]

To determine if a string contains a substring, use the includesSubstring: method. To replace every occurrence of a substring with another string, use the copyReplaceAll:with: method.

To get a substring from a string, use the copyFrom:to: method as follows:

s1 := 'abcde'.
s2 := s1 copyFrom: 2 to: 4. "sets s2 to 'bcd'"
s3 := s1 copyFrom: 3 to: s1 size. "sets s3 to 'cde'"

To get a substring that includes all characters but beginning ones use the allButFirst and allButFirst: methods. To get a substring that includes all characters but ending ones use the allButLast and allButLast: method.

Symbols

Symbols are names that are preceded by a #. They are used in place of strings when there should only one instance with the name. If a symbol name contains special characters such as spaces, periods or underscores, surround the name with single quotes.

Regular Expressions

Amazingly the standard Squeak distribution doesn't include support for regular expressions. However, Squeak-Dev distributions include the VB-Regex package. VB stands for Vassili Bykov, the author. Among other things, this package adds methods to the String class such as matchesRegex:. See http://www.dartois-d.nom.fr/regex/en/ for documentation.

'a1b' matchesRegex: '\w\d\w' "answers true"

Other Types

Types that are primitives in other programming languages are described by classes in Smalltalk. For example, the "Kernel-Numbers" category contains the classes Float (double-precision), Fraction and Integer which are subclasses of Number. The "Kernel-Objects" category contains the classes Boolean, False, True and Object.

Constants

Constants are typically held in class variables. They are initialized in a class initialize method. This is automatically called when classes are loaded using Monticello. What calls it if this isn't done?

EnumeratedTypes

Smalltalk doesn't support the concept of enumerated types like in Java 5 and above. Instead, the Smalltalk way is to:

  1. create a class that represents the enumerated type
  2. add a class variable for each enumerated value (must start uppercase)
  3. add a class-side initialize method that creates an instance of the class for each enumerated value using basicNew and assigns it the corresponding class variable
  4. prevent creation of additional instances by overiding the class method new with "self error: 'new instances cannot be created'"
  5. add class-side getter method for each enumerated value that simply returns it

Here's an example ColorEnum class.

Object subclass: #ColorEnum
  instanceVariableNames: ''
  classVariableNames: 'Blue Green Red'
  poolDictionaries: ''
  category: 'SomeCategory'

initialize
  Red := self basicNew.
  Green := self basicNew.
  Blue := self basicNew

new
  self error: 'new instances cannot be created'

red
  ^Red

green
  ^Green

blue
  ^Blue

Comments

Comments are surrounded by double quotes and can span multiple lines.

To add "todo" comments that can be easily found later, enter "self flag: #todo." followed by a regular comment which can be on the same line. To find all occurrences of that, select "todo" and press alt-n which displays all the senders of #todo in a new window. In OmniBrowser, methods containing those have a wrench icon in front of them in the method plane.

Literal/Dynamic Arrays

These are Squeak extensions, not standard Smalltalk syntax.

Literal arrays create an array from a list of literal values. The code numbers := #(1 3 7) creates an array of Integer objects. The code colors := #(red green blue) creates an array of symbols. The code colors := #('red' 'green' 'blue') creates an array of String objects.

Dynamic arrays (a.k.a. bracket arrays) create an array from expressions separated by periods. The code colors := {'red' asUppercase. 'green' asUppercase. 'blue' asUppercase} creates an array of String objects that are the results of the asUppercase method.

Reserved Keywords

There are only six reserved keywords.

  1. self refers to the current object.
  2. super is used to force method lookup to begin in the superclass.
  3. nil represents no value and is a singleton instance of the class UndefinedObject.
  4. true and false are boolean values that are singleton instances of the classes True and False.
  5. thisContext represents the top frame of the call stack which represents the method or block that is currently executing.

Logical Expressions

The Boolean class defines many methods for forming logical expressions. and: and or: short-circuit. & and | do not. Other methods include not and xor:.

Naming Conventions

Names of local/temporary variables and instance variables start with a lowercase letter and are camelcased. Names of classes, class variables and global variables start with an uppercase letter and are camelcased.

Indentation

The convention in Squeak Smalltalk is to use a single tab.

Statements

There are three kinds of statements in Smalltalk.

  1. Message sends send a message to a specified object. They begin by specifying the "receiver" of the message. To send a message to the current object, use the keyword self or super for the receiver. There are three kinds of messages: unary, binary and keyword. For more on these, see the Messages section below. Messages can be "cascaded" to send multiple messages to the same object. This is done by separating the messages with semicolons.
  2. Assignments assign a value to a variable. Their syntax is variable := expression where expression is a literal value, variable or message send.
  3. Answers specify the return value of a method. Their syntax is ^expression.

There is also a syntax for defining methods, but this is hidden by the development environment and isn't usually viewed directly.

There are no statements for branching or looping. Instead, those things are performed by methods in classes like Boolean, Interval and Collection.

Here's an example if-then-else. "ifTrue:ifFalse:" is a method of Boolean which is what "m > n" returns. The show method of Transcript writes to the Transcript window if one is open.

m := 4.
n := 5.
m > n
  ifTrue: [
    Transcript show: 'm is bigger'; cr
  ]
  ifFalse: [
    Transcript show: 'n is bigger'; cr
  ]

The equivalent of a switch/case statement is the Object method caseOf:otherwise:.

Here's an example loop through a range of numbers. "to:" is an Integer method that creates an Interval object. "do:" is an Interval method that executes a block for each value in it.

3 to: 7 do: [ :i | Transcript show: i * 2; cr ]

Here's an example loop through an Array of symbols.

array := #(red green blue).
array do: [:item | Transcript show: item; cr]

Messages

There are three kinds of messages that can be sent to classes and objects.

  1. Unary messages take no arguments (ex. 5 factorial).
  2. Binary messages take one argument (ex. 2 + 3). Such methods should end with ^self to support chaining of calls. These are mainly used for mathematical operators. For other methods that take one argument, keyword messages are used. For a complete list of binary messages, see Binary Messages.
  3. Keyword messages take any number of named arguments (ex. 19 printPaddedWith: $+ to: 5. which returns '+++19'). Argument names typically describe the kind of value they expect (for example, aBoolean). Despite being named, the arguments must be specified in the order in which they are listed in the method definition. When describing keyword messages, the arguments names are run together. For example, the Dictionary class contains a method described by at:put:.

Any number of messages can be sent to the same object by chaining them using semicolons. For example,

s := Set new.
s add: 1; add: 2; add: 3.

Binary Messages

Characters that look like operators from other programming languages are binary messages in Smalltalk. Many are defined in provided classes and they can also be defined in custom classes. There are a fixed set of them and they are all listed below.

Message Precedence

Smalltalk doesn't use operator precedence when evaluating statements. Instead it evaluates statements in the following order unless parentheses are used to override the order.

  1. unary messages from left to right
  2. binary messages from left to right
  3. keyword messages from left to right
  4. assignment

This means that a := 2 + 3 * 4 will result in a being set to 20, not 14 as is the case in most other programming languages. To get 14, use a := 2 + (3 * 4).

This also methods that parentheses are needed in "v = 1 | v = 2" because all the messages are binary and will be evaluated left to right. The correct code is "(v = 1) | (v = 2)".

Blocks

A block is a sequence of statements whose execution is deferred. They can be thought of as methods with no name (anonymous) Blocks can take parameters and return a value. They are represented by instances of the BlockClosure class which is in the Kernel-Contexts category.

The syntax for a block is [ argument-list | statement-list ]. This makes sense since real blocks have square corners. Names in the argument list are preceded by colons and they are separated by spaces. For example, "[ :p1 :p2 | code ]". If there are no arguments then the vertical bar can be omitted. Before the statements, a list of temporary variables can be declared in the same way they are declared at the beginning of a method. Statements in the statement list are separated by periods. The Seaside coding conventions call for a single space inside the square brackets.

Blocks can be passed as arguments to methods. Once inside the a method, block arguments can be executed by sending them many different methods from the BlockClosure class. Examples include value (no args), value: (1 arg), valueWithArguments: (any number of args in an array), repeat, doWhileFalse: and doWhileTrue:.

Blocks are closures which means they remember the variables that were in scope when they were created.

To run the code in a block in another process (or green thread), see the methods starting with "fork" in the BlockClosure class. To "sleep" within a process, see the Delay class.

Variables

There are five kinds of variables in Smalltalk.

  1. Local variables are only visible in the scope of a method or block. They are listed between vertical bars and separated by spaces at the top of method definitions. For example, | var1 var2 var3 |.
  2. Instance variables are only visible in instance methods of their class. They can be referenced either with only their name or their name preceded by self. They are listed in a space-separated string that is the value of the class definition instanceVariableNames: argument.
  3. Class variables are only visible in class and instance methods of their class. They are listed in a space-separated string that is the value of the class definition classVariableNames: argument.
  4. Global variables are visible everywhere and are maintained by the Smalltalk object which is a global SystemDictionary object. Most of the entries in the dictionary refer to Class objects.
  5. Pool dictionaries hold variables that are available to all classes that list the dictionary name in a space-separated string that is the value of the class definition poolDictionaries: argument.

Methods

A method is an implementation of a message. Methods are described by the message that invokes them (called the method signature in other languages), a comment, a list of local/temporary variables inside vertical bars and a sequence of statements separated by periods. The method signature can be just a method name (unary), a method name followed by a single argument name (binary), or list of keyword and argument name pairs where each keyword is terminated with a colon (keyword). The list of temporary variables is a space-separated list of variable names surrounded by vertical bars. The reason its good to have to declare variables in this way is to make code typo-proof. If variables just sprang into existence the first time they were used, typos in variables names would create new variables rather than being flagged as errors. The Seaside coding conventions call for a single space inside the vertical bars.

It is a standard in Smalltalk to indent code using tabs instead of spaces.

It's not necessary to manually enter the local/temporary variables between the vertical bars that declare them. When saving the method, the compiler spots undeclared variables and offers the choice of automatically declaring them as either instance or temporary variables. It does this by displaying a dialog with "Unknown variable: {name}" in the title bar and the choices "declare temp", "declare instance" and "cancel".

A method can return a value using ^expression. If a value is returned then by convention its comment should begin with "answers ...". Methods that don't explicitly return a value will return self. When methods are cascaded, the value returned by the last method is the value of the expression. To make the return value of a cascaded sequence of messages be the original receiver instead, send "yourself" as the last message. This is often useful in conjunction with creating a new object that is returned from a method. For example,

  ^Car new
    make: 'BMW';
    model: 'Z3';
    paint: Color yellow;
    yourself

All methods are accessible to all other methods. The fact that some methods are intended for use only by other methods in the same class is documented by placing them in a protocol named "private".

Accessors

Accessor methods for getting and setting the values of instance variables do not, by convention, begin with "get" and "set". For an instance variable "foo", the getter method should be named "foo" and the setter method should be named "foo:".

Accessor methods for all the instance variables can be generated by yellow-clicking in the edit pane and selecting refactor class...accessors. Modify generated setters to have an argument name that indicates the type of object expected.

Exception Handling

Exceptions are objects from classes that inherit from the Exception class. Browse the Exception class and press the "hierarchy" button to see the predefined subclasses. There are two direct subclasses, Abort and Error. All the other predefined exceptions inherit from Error.

To "signal" (throw in Java) an exception, create the exception object and send the signal message to it.

To catch an exception, invoke code that might raise it in a block and send a message for a BlockClosure method such as on:do: to it.

Here's an example.

thrower
  "demonstrates throwing an exception"
  | e |
  e := Exception new.
  e messageText: 'something bad happened'.
  e signal.

catcher
  "demonstrates catching an exception"
  [ self thrower ] on: Exception do: [ :e |
    Transcript show: e messageText
  ]

The BlockClosure class has an ensure: method that executes a given block regardless of whether an exception is signaled (similar to a Java finally block). It also has an ifCurtailed: method that only executes the block if an exception is signaled.

Also look at these methods in the Exception class: pass, resignalAs:, resume, retry, retryUsing, return, return:

Stream I/O

I/O is performed using subclasses of the Stream class. To read or write a text file, use a FileStream which is a subclass of ReadWriteStream.

The following code writes to a new text file. The nextPutAll method writes all elements of a collection to the stream. Note that the String class inherits from AbstractCollection. Each character in the string is an element of the collection.

fs := FileStream newFileNamed: 'foo.txt'.
fs nextPutAll: 'line 1'; cr.
fs nextPutAll: 'line 2'.
fs.close

The following code reads the text file.

fs := FileStream fileNamed: 'foo.txt'.
[fs atEnd] whileFalse: [
  | line |
  line := fs upTo: Character cr. 
  Transcript show: line.
]. 
fs.close

To obtain the content of a stream as a String, send the contents message to it.

The close method doesn't do anything. Data can continue to be written to the stream after it is called.

To work with stdin and stdout of the current process, load and use the OSProcess package. The README file says that Mac OS X isn't supported yet. To load the OSProcess package, red-click the desktop and select the following: open..., Universe Browser (basic), System, OSProcess, "select package" button and "Install Selections" button. Here's an example of using it:

process := ThisOSProcess thisOSProcess.
stdin := process stdIn. "acts like a Stream"
stdout := process stdOut.
stderr := process stdErr.
stderr print: Time now; cr. "put the time of day on my stderr output"

Benchmarks

To find out how long it takes to run a section of code, do the following:

ms := [
  code goes here
] timeToRun

Reflection

To determine whether a variable refers to an object of a given type use isMemberOf (instance of given class) or isKindOf (instance of given class or a subclass). For example:

v := 5.
(v isKindOf: Integer) ifTrue: [ Transcript show: 'an Integer!' ]

Web Applications

The most popular framework for developing web applications using Smalltalk is Seaside.

Squeak Environment

Squeak is a descendent of Smalltalk-80. It was created by Alan Kay and Dan Ingalls while they worked at Apple, and continued at Disney. It is moving toward, but not yet compliant, with ANSI Smalltalk. Squeak can be downloaded from http://www.squeak.org/Download/. For versions targeted at developers, look for "Squeak-Dev" on that page.

There are four files associated with Squeak.

  1. a machine-specific virtual machine (VM)
  2. an image file that stores objects, including those representing the current state of the user interface
  3. a .sources file that contains all the source code
  4. a .changes file that stores changes you make to the source, including classes you add

The Squeak virtual machine is implmented entirely in Smalltalk code and tested that way. When a new release is made, that Smalltalk code is run through a tool that translates it to C code which is compiled and linked to create a new VM executable.

Prebuilt versions of Squeak can be downloaded for many platforms. To get Squeak for Ubuntu, select Applications...Add/Remove..., select the "All" category, and search for "Squeak".

World Menu

The main menu in Squeak is displayed by yellow-clicking on the desktop. This is called the "World menu". A large amount of functionality can be accessed from this menu.

Starting and Quitting

To start Squeak, double-click on a .image file. To quit Squeak, select World...save and quit or World...quit.

Windows

To collapse or uncollapse a window click the circle icon on the far right end of the title bar.

Configuration

Here are some common configuration changes for new images.

Mouse Buttons

In Smalltalk development environments the mouse buttons are referred to as red, yellow and blue. These are mapped differently under different OSes. The mappings are summarized in the table below.

Button Windows Mac OS X Linux/UNIX
red left left left
yellow right option-click or wheel-click middle
blue alt-left right or cmd-click or ctrl-click right

The yellow and blue buttons can be swapped by opening the World menu, selecting "Preference Browser", selecting "developer image", and enabling "swapMouseButtons".

Flaps

"Flaps" are tabs that appear around the edges of the main window. Red-clicking them opens a drawer full of tools/applications. Red-clicking them again, or red-clicking another flap on the same edge, closes the drawer. To enable repositioning of the flaps, turn off "automatic flap layout".

To move all the flags to the bottom edge of the main window, open the World menu, select "flaps..." and select "put shared flaps on bottom".

Keyboard Shortcuts

Many keyboard shortcuts start with the ctrl or alt key. On a Mac the command (Apple) key can be used instead of either of these in most cases.

To find given text in the current method, press ctrl-f and enter the text in a dialog.
To find the next occurrence, press ctrl-g.
To set the search string to the text that is selected, press ctrl-h.
The text that is found can be replaced by typing over it while it is highlighted.
To make the same replacement in the next occurrence, press ctrl-j.
To make the same replacement in all occurrences, press ctrl-J.

To find all occurrences of given text in all methods, enter the text in a window such as a Workspace, select it, yellow-click, and select more...method source with it. Other items on the more menu include "selectors containing it", "method strings with it", "class names containing it" and "class comments with it".

Workspace

This tool is useful for trying snippets of code. To get one, drag one out of the "Tools" flap or select World...open...workspace. Enter expresssions, select them and use commands such as browse it (ctrl-b), do it (ctrl-d), explore it (ctrl-I), inspect it (ctrl-i) and print it (ctrl-p).

Transcript

This tool is useful for viewing the output of code. To get one, drag one out of the "Tools" flap or select World...open...transcript. To send output to it, enter "Transcript show: expression" in a Workspace and do it. This is basically equivalent to System.out.print in Java. To add a carriage return and make it like System.out.println, enter "Transcript show: expression; cr". To clear the window, press ctrl-a to select everything and press delete or enter "Transcript clear". When more than one Transcript window is open, output goes to all of them.

The Smalltalk equivalent of Java's toString method, which returns the string represenation of an object, is the printString method. This is invoked when a "print it" is performed on an object.

A new, labelled Transcript window can be opened from code.

ts := TranscriptStream new.
ts open. "opens window with default title of Transcript".
ts openLabel: 'My Transcript'. "opens window with custom title"
ts show: 'Hello'

For simple cases, this can be combined on one line as "TranscriptStream new open; show: 'some message'".

Transcript is a global variable that refers to a TranscriptStream.

Another option for displaying small amounts of output is to use "self inform: some-string" which displays a string in a dialog.

System Browser

A "browser" allows viewing and editing of classes and methods. One way to open a browser is to open the "Tools" flap and drag out a "System Browser". Another way is to enter "class-name browse" in a Workspace and "do it" by pressing ctrl-d.

Classes are grouped in categories. Methods within classes are grouped in protocols. Common protocols include "accessing", "adding", "comparing", "converting", "copying", "displaying", "enumerating", "formatting", "instance creation", "printing", "removing" and "testing". The System Browser has four panes across the top for categories, classes, protocols and methods. The bottom pane, called the "edit pane", is for browsing, editing and adding classes and methods.

To select a category, click on it's name in the category pane. To scroll closer to it, click in the cagtegory pane and type the first letter.

The buttons at the bottom of the class pane control what is displayed in other panes. Press the "instance" button to display instance methods in the method pane. Press the "class" button to display class methods in the method pane. Press the "?" button to see the comment for the selected class in the edit pane.

To move a method to another protocol, drag its name from the method pane to its new protocol in the protocol pane.

The buttons above the edit pane control what is displayed in other windows. To display a Hierarchy Browser for the current class, double-click it in the class pane or select it in the class pane and press the "hierarchy" button or ctrl-h. This displays all superclasses and subclasses of the current class. Unlike the System Browser, this tool allows browsing all the classes in the inheritance hierarchy of a given class in a single window. Another way to see the superclasses and subclasses of a given class is to enter "Class-Name printHierarchy" in a Workspace and "print it" (ctrl-p).

The last button at the top of the edit pane defaults to "source". To enable pretty printing (formatting) of the code that happens automatically when changes are saved, press the "source" button and select "pretty print" or "color print".

When a method is selected so that its code is displayed in the edit pane, to view its class definition in the edit pane, yellow-click the method to deselect it.

OmniBrowser

In a Squeak-Dev image it defaults to using "OB Package Browser" in place of "System Browser". This is also known as the "OmniBrowser".

Unlike the System Browser, this has back (<<) and forward (>>) buttons just above the editor pane for moving between recently browsed classes.

In the editor pane, by default it automatically outputs matching characters as you type for single quotes, double quotes, parentheses, square brackets, and so on. If you find this annoying, it can be disabled. On the World menu, select "Preferences & Services"..."Preference Browser". Select the "developer image" category. Disable ecompletionSmartCharacters.

Icons in front of method names provide extra information. For a description of what they mean, see http://smallwiki.unibe.ch/hermion/icons/.

It get hints on issues with code, yellow-click a category, class or method and select open...code critics.

Variable References

To find all the references to a given instance or class variable within a class, yellow-click the class in OmniBrowser and select an option from the open environment submenu such as class variable references, instance variable reader, instance variable references, and instance variable writer.

Inspector

An Inspector is used to examine the variables in a given object. Select a variable in the upper-left pane to display its value in the upper-right pane. The value in the upper-right pane can be modified. After changing it, press ctrl-s to save the change. The lower pane is used to enter code can be executed. For example, when inspecting a Dictionary object the code self at: 'foo' put: 'bar' can be entered. To execute this, select the code and press ctrl-d (do it).

Method Finder

This tool is useful for viewing the output of code. To get one, drag one out of the "Tools" flap or select World...open...method finder. Enter part of a method name and press enter to get a list of all method names that contain it. Click a method name in the list to see the class that defines it.

To find all messages containing the selected string, press ctrl-W.

File List

This tool is useful for viewing files in the file system and performing "fileins" on .st files. To get one, drag one out of the "Tools" flap or select World...open...file list. To open a text file from the file system, navigate to the directory in the left pane and select the file in the right pane. If the file contains Smalltalk code (*.st), add it to the image by pressing the "fileIn" button at the far right end of the button bar To change the way files in the right pane are sorted press the "name", "date" or "size" buttons in the button bar.

Senders

To find the methods that send a given message, select the message in a browser or enter it in a Workspace and press alt-n. This is a great way to find example uses of a message.

Implementers

To find the classes that implement a given message, select the message in a browser or enter it in a Workspace and press alt-m.

Browse/Do/Inspect/Print It

When no text is selected, these commands operate on the entire current line.

To browse a class in a new System Browser, select its name in any window and press ctrl-b. To use the same System Browser, press ctrl-B. To choose from a list of recently browsed classes, yellow-click in the category pane and select "recent classes" or press ctrl-r.

To print the result of the last statement in the Workspace, press ctrl-p or yellow-click and select "print it". The output will be selected. Press the delete key to delete it.

To inspect the result of the last statement in an Inspector dialog, press ctrl-i or yellow-click and select "inspect it". This shows the class of the result in the title bar and allows all instance variables of the object to be examined.

To execute the statements without printing, press ctrl-d or yellow-click and select "do it".

Packages

Packages are Smalltalk applications and libraries that can be downloaded and installed in your image. One way to download new packages or upgrade existig packages is to press "Package Universe Browser" button on the desktop. Another way is to open the "Tools" flap and drag out a SqueakMap "Package Loader".

In the Package Universe Browser, press the "update list from network" button to get a list of available packages. For each package to be installed, click its name in the left pane and press the "select package" button. After all the selections have been made, press the "Install Selections" button at the bottom. Press the "select all upgrades" button to select upgrades to all currently installed packages.

In the SqueakMap Package Loader, red-click a category in the lower-left pane to limit the packages displayed in the upper-left pane. Red-click a package name in the upper-left pane to see a description of it in the right pane. Yellow-click a package in the upper-right pane and select "Install" to install it.

Another tool to examine is Monticello.

Create Category

To create a new category, yellow-click in the category pane of a System Browser and select "add item...".

Create Class

To create a new class, red-click its category in a System Browser. This will display a template for the new class in the bottom pane with the following content:

Object subclass: #NameOfSubclass
  instanceVariableNames: ''
  classVariableNames: ''
  poolDictionaries: ''
  category: 'category-name'

This creates a new class by sending a message to its superclass. Modify the base class if necessary (defaults to Object) and change "NameOfSubclass" to the name of the new class as a symbol. Add names of instance variables, class variables and pool dictionaries. Press ctrl-s to save it. The class will be given a comment of "THIS CLASS HAS NO COMMENT!". Change that to a better comment and press ctrl-s again.

Classes are represented by objects from the Class class that are referred to by a global variable with the same name as the class.

Delete Class

Select the class in the class pane of a System Browser and press ctrl-x or yellow-click the class name and select "remove class".

Find Class

To find a class in a System Browser when you don't know what category it is in, click in the category pane and press ctrl-f or yellow-click in the category pane and select "find class...". Enter all or part of the name. Select the class from the list of matching classes. It's category will be highlighted in the category pane and also shown in the class definition after "category:".

To browse a class that has been browsed recently, yellow-click in the category pane and select "recent classes...".

To browse a class whose name is selected in the bottom pane of a System Browser, press ctrl-b.

Create New Object

To create an object from a class named "Foo" and assign it to a variable, use "foo := Foo new.". This invokes the new method in the Class object for the Foo class. The Class class is a subclass of the Behavior class which defines the new method. That calls basicNew and initialize. If the class has an instance initialize method, it will be invoked. This is a good place to initialize instance variables to default values. In most cases, the first thing an initialize method should do is send the initialize message to its superclass with super initialize.

If data must be specified in order to properly construct new objects, write class methods that accept the data, create a new instance, and initialize it with the supplied data. For example:

create: total
  "creates a new Math instance with a given starting value for total"
  ^self new
    total: total;
    yourself

This can be invoked as follows: "m := Math create: 3.".

There are five ways, involving four classes, in which objects can be created without using the new method.

  1. String - 'text'
  2. Point - x@y
  3. BlockClosure - [ statements ]
  4. literal Array - #( element1 element2 ... )
  5. dynamic Array - { expression1. expression2. ... }

All of these involve syntax that the compiler must recognize except @ which is a binary method in the Number class. The last two that create arrays are specific to Squeak.

Browse or Edit Method

To browse or edit an existing method in a System Browser:

  1. select its class
  2. select its protocol or "-- all --"
  3. select the "instance" or "class" button at the bottom of the class pane (only methods of that type will be listed in the method pane)
  4. select the method name

The code will be displayed in the edit pane.

Add Method

To add a method to a class, select the class in a System Browser, press either the "instance" or "class" button depending on the method type, select the protocol for the method (may need to add a new one), modify the code template for the method in the bottom pane, and press ctrl-s to compile it to bytecode and save it.

Here's an example of a class method in a class named "Math". It uses keyword parameters "add" and "and". It uses a temporary variable named "sum". It returns a value (^). Note the lack of a period at the end of the last line.

add: n1 and: n2
  "adds two numbers"

  | sum |
  sum := n1 + n2.
  ^sum

Here's an example of some instance method in a class named "Math".

initialize
  "initializes a new Math object"
  total := 0

total
  "retrieves the running total"
  ^total

add: value
  "adds value to the running total"
  total := total + value

Delete Method

To delete a method, yellow-click its name in the method pane of a System Browser and select "remove method".

Method Arguments

Method arguments cannot have default values. To simulate this, create overloaded methods where those that take fewer parameters call those that take more with default values for their missing parameters.

Code Formatting

To format the code of the method being viewed in a browser, yellow-click and select "pretty print".

The class that controls pretty print is RBFormatter. It can be customized. For example, the method maxArgumentsPerLine defaults to 1 which causes each keyword in a keyword message to be on a separate line. Consider changing this to 5.

Keyboard Input

TODO: How does this work? One way to prompt the user for input is to use the FillInTheBlank class. For example, name := FillInTheBlank request: 'Enter your name'.

Filein/Fileout

The code for a class can be saved to a text file, possibly edited outside the Squeak enviroment, and read back in. To save the code for a class to a text file, yellow-click the class in a System Browser class pane and select "fileout". A file with the same name as the class and a ".st" file extension will be written to the directory containing the currently running image. To read in a text file containing code for a class, open the "Tools" flap, drag out a "File List", select the file, and press the "filein" button in the upper-right corner.

Unit Tests

The preferred framework for implementating unit tests is SUnit. Create classes that inherit from TestCase. In the protocol "running", add setUp and tearDown methods if needed. In the protocol "tests", add methods that begin with "test".

In the test methods, use "self assert: (boolean-expression)" and "self deny: (boolean-expression)" to specify assertions. For testing exceptions there are should:raise: and shouldnt:raise: methods. All of these assertion methods take an optional description: argument.

When an assertion fails, it's description string is displayed in the title bar of the window that is displayed when a failed test is selected. The description strings are also displayed in the Transcript window if the test case overrides the isLogging method to return true.

To run the tests, open the Tools flap and drag out an "SUnit Runner". Select the categories and test classes to be run. Press the "Run Selected" button. If all the tests pass, the bar in the upper-right will be green. If test failures occur, the bar will be yellow and the test methods with failures will be listed in the right-middle panel. If errors are encountered, the bar will be red and the test methods with errors will be listed in the right-bottom panel. Clicking on the name of a test method with a failure or error will display a window containing a stack trace (referred to as a "walkback stack") for the method.

In OmniBrowser, all the tests in a category can be run by yellow-clicking a category in the category pane and selecting "run the tests" or pressing ctrl-t. A summary of the results is displayed in a dialog, but details are not displayed in a "Test Runner" window.

Debugger

To set breakpoints, add "self halt." statements in the code. When a breakpoint is hit, a debugger window will be displayed. It allows inspection and modification of instance and local/temporary variables. Source code can be viewed and modified in the debugger. It also provide buttons for controlling continued execution such as Into (step into a message send), Over (step over a message send), Through (step into a block), Proceed (close debugger and continue) and Restart (go back to beginning of current method).

In OmniBrowser, methods containing a call to "self halt." will have a red flag icon in front of them in the method pane.

To debug code in a Workspace, select the code, yellow-click and select "debug it".

Monticello

This tool is a distributed versioning system. It is the primary means in which Squeak developers backup and distribute their code. To open a Monticello Browser, drag one out of the Tools flap or select World...open...Monticello Browser.

To backup a package locally, select it in the left pane, select the "package-cache" directory in the right pane, and press the "Save" button. This will create a .mcz file which is a Monticello zip file containing the code for the package (snapshot/source.st) and a few other files.

To save a package on SqueakSource:

  1. Browse to http://www.squeaksource.com.
  2. If you haven't yet registered, click the "Register Member" link to register yourself. Otherwise, click the "Login" link and login.
  3. Click the "Register Project" link to register your project. The URL for your project will be http://www.squeaksource.com/{project-name}.
  4. In the Monicello Browser, press the "+Repository" button.
  5. Select "HTTP".
  6. In the HTTP Repository template that is displayed, change the location to the project URL, change the user to your initials as entered in SqueakSource, and change the password to what was entered in SqueakSource.
  7. Select the package to be saved in the left pane.
  8. Select the SqueakSource repository URL in the right pane.
  9. Press the "Save" button.
  10. Enter a comment for the new version.
  11. A new version of the project will be saved on SqueakSource and also in the local package-cache directory.
  12. Repeat steps 7 through 10 each time significant changes are made to the package.

To retrieve a package from SqueakSource:

  1. In the Monicello Browser, press the "+Repository" button.
  2. Select "HTTP".
  3. In the HTTP Repository template that is displayed, change the location to the project URL, change the user to your initials as entered in SqueakSource, and change the password to what was entered in SqueakSource.
  4. Select the package URL to be retreived in the right pane.
  5. Press the "Open" button.
  6. In the "Repository" dialog that is displayed, select the ".mcz" file and press the "Load" button.
  7. The package should now appear in System Browsers.

Refactoring

There are many refactoring options available in browsers. Yellow-click a class in the class pane or a method in the method pane and select an item from the refactor submenus.

Fonts

To change the fonts used in the Squeak environment, open the World menu and select appearance...system fonts... Fonts used in existing windows won't be changed. To see new fonts, close the windows and open new ones. A good font is Accujen size 10 or 12. To install additional True Type fonts (.ttf files), download them, open a File List tool, select a .ttf file, and press the "install ttf" button.

SVI

SVI is a text editor for Squeak that supports Vim and Emacs commands. It is used in place of the default PluggableTextMorph. To install it, open a World menu, select open...SqueakMap Package Loader, open the SVI tree node, yellow-click the version you want, and select "install. This didn't work! I'm trying to find a solution.

Projects

Squeak projects are like separate working environments, each with its own set of open windows on the desktop ... like virtual desktops. The can be used to separate work on different projects, or separate work on difference aspects of the same project (such as model classes vs. view classes). They can improve coding efficiency because switching between projects is faster than opening or expanding Browsers.

On the World menu there are items to create a new project ("open...morphic project"), switch to the previous project ("previous project"), switch to a selected project ("jump to project..."), save a project to a file ("save project on file...") and load a project from a file ("load project from file..."). The main or default project is named "New Changes".

After creating a new project, give it a name by changing the text at the bottom of the mini project window icon.

Projects are saved in ".pr" files. When saved locally, they are in the Squeaklets subdirectory of the current image.

Projects can be nested.

Morphic

Morphic is a library for building Squeak GUIs. A "morph" is similar to a widget in other GUI toolkits. Supplied morphs include CheckboxMorph, DropDownChoiceMorph, DropListMorph, ImageMorph, LabelMorph, MenuItemMorph, MovieMorph, ProgressBarMorph, SimpleButtonMorph, TextFieldMorph and TreeListMorph. All the morphs visible on the desktop are submorphs of the "world" morph. The mouse cursor is referred to as the "hand" and it is an instance of HandMorph.

Here's a simple example of creating a button that when pressed displays text in a Transcript window.

button := PluggableButtonMorph
  on: [Transcript show: 'You pressed it!'; cr]
  getState: nil 
  action: #value.
button label: 'Press Me!'.
button position: 100@100; openInWorld

Morph layout can be done automatically or it can be controlled by layout morphs. The most commonly used of these is AlignmentMorph which arranges morphs from left to right in a single row or from top to bottom in a single column. The AlignmentMorph newSpacer: method adds space between morphs.

User interations with morphs are represented by MorphicEvent objects. When mouse and keyboard events occur, messages such as mouseDown, mouseMove, mouseUp and keyStroke are sent to morph objects.

If a morph is dropped on another morph, the target morph is asked whether it can handle the dropped morph by sending the message wantsDroppedMorph: aMorph event: evt to it. If this returns true then the message wantsDroppedMorph: aMorph event: evt is sent to it. After the target processes the drop, the source of the drop will be sent the message justDroppedInto: aMorph event: evt.

To determine the class of a morph that is currently displayed, blue-click to get the halo, press the "debug" button (the wrench), and select "browse morph class". Windows are created using the SystemWindow class. Select "explore morph" to see the "submorphs".

For more inforamation, see An Introduction to Morphic.

There are many alternatives to Morphic. These include Polymorphic, Tweak and wxSqueak.

Startup and Shutdown

When a Squeak image starts up, it calls the class startUp method in each class listed in the collection referred to by the SystemDictionary class variable StartUpList. When a Squeak image shuts down, it calls the class shutDown method in each class listed in the collection referred to by the SystemDictionary class variable ShutDownList. The SystemDictionary class initialize method adds many classes to these lists.

The only instance of SystemDictionary is held in the global variable Smalltalk. Classes are added to these lists by sending the messages addToStartUpList and addToShutDownList to Smalltalk. Classes are removed from these lists by sending the messages removeFromStartUpList and removeFromShutDownList to Smalltalk.

To see what is currently in these lists, view the class definition of SystemDictionary in a browser, select the class variable StartUpList or ShutDownList, and press ctrl-I to "explore it". In some cases it's important for the order of classes in these lists to be opposite of each other.

Running Headless

Under Mac OS X, to make "squeak" available as a command, open a Terminal window and cd to any directory in your PATH. Create a symbolic link by entering "ln -s "$SQUEAK_VM_DIR/Squeakversion.app/Contents/MacOS/Squeak VM Opt" squeak". When Squeak is started from a Terminal window using this command it doesn't run properly. It doesn't respond to mouse events and the -headless option doesn't work!

To run a Smalltalk script without starting the GUI, create a text file with a ".st" file extension, add a sequence of Smalltalk statements in it, and run "squeak -headless $SQUEAK_IMAGE full-file-path.st".

Here's an example of such a script. Note that temporary variables don't need to be declared.

"The file is written in the directory of the image."
fs := FileStream fileNamed: 'FileIODemo.txt'.
fs nextPutAll: 'line 1'; cr.
fs nextPutAll: 'line 2'.
fs close.

This runs the script, but there are two issues. First, a splash screen is displayed. To prevent this, rename or delete the "splash.bmp" file in the Squeak VM directory. Second, the VM doesn't exit automatically. To make it exit when the script finishes, add the following line to the end of the script.

SmalltalkImage current quitPrimitive

Persistence

There are many ways to implement persistence in Smalltalk including ActiveRecord (from Cincom), GLORP, GOODS, Gemstone/S, ImageSegments, Magma, OmniBase, PostgreSQL, Prevayler, ReferenceStreams, Roe, SIXX, SQLite, and simply saving the image.

Foreign Function Interface (FFI)

FFI is used to call functions that are typically implemented in C.

Miscellaneous

The remaining information either needs to be merged with the information above or deleted.

From Matthew Fulmer:
When you run code, it is compiled into a method called UndefinedObject>>DoIt,
then that method is run, then deleted.
The source code of that method is the code you run.
It happens when running a doit from the workspace, or when filing-in code.

Squeak version control is automatic! (describe this)
    

Copyright © 2008 Object Computing, Inc. All rights reserved.