The key to understanding Smalltalk is not its syntax (which is very small), but its concept. Smalltalk is pure object-orientation. Everything is an object: integers, strings, other instances of classes, the classes themselves, and even blocks of code. The only way to accomplish something in Smalltalk is to let these objects send messages to each other. Messages are sent to objects by writing the name of the message (the message selector) behind the object the message is sent to.
st> 'Hello World' printNl ! 'Hello World'
What does this example tell us? First, Smalltalk strings are enclosed in single quotes. Double quotes are used for comments. Second, strings seem to understand the printNl and print themselves including the quotes. And finally, as GNU Smalltalk specialty, statements are finished with an exclamation mark (most other implementations use a period). Something you will appreciate about Smalltalk is its consistency. The message printNl is understood by any object, and, as we said in the beginning, everything is an object, even "primitive" types.
st> 1234 printNl ! 1234 st> 1.234 printNl ! 1.234
For printing object the message passing might make sense, but how is this supposed to work for, say, an expression such as "4 + 5"?
st> (4 + 5) printNl ! 9 st> (2 + 3 * 4) printNl ! 20 st> (2 + (3 * 4)) printNl ! 14 st> (5 negated) printNl ! -5
The expression looks like a normal arithmetical expression, but behind the scenes Smalltalk interprets the first expression as the message "add 5" sent to the object "4". The second expression first sends the message "add 3" to the number "2". The resulting object "5" is then told to "multiply with 4". Message passing is simple evaluated from left to right, and you have to take care of the preference rules yourself. As a prefix operator, the minus sign does not fit into the message passing concept. Instead you need to send the "negated" message to a number to achieve the same effect.
The arithmetical operators are special because messages with arguments normally have method selectors ending with a colon. For example, sending the message "to: 10" to an integer n creates the interval from n to 10 (including, no half open interval semantics like in Python).
st> (1 to: 10) printNl ! Interval(1 2 ... 10) st> ((1 to: 10) at: 2) printNl ! 2
And for any sequence object, the message "at:" followed by an index n gives us the n'th element (counting from 1 unlike the languages influenced by C).
To make things more interesting, we have to introduce a variable in the environment of the Smalltalk interpreter.
st> Smalltalk at: #x put: 0 ! st> x printNl ! 0 st> x := 'Hello' ! st> x printNl ! 'Hello'
Don't worry about the strange name "#x". It is just the name "x", but considered a unique symbol (in contract to strings which allow multiple objects to have the same value 'x'). One the variable exists, we can assign to it using the assignment operator ":=". Variables are not bound to a type. Like in Python, a variable can first contain an integer and later be changed to a string (or any other object).
At this point we still wonder if the wonderful world of message passing makes live easier or harder. Just consider control statements such as if clauses and loops. To see how those can expressed quite elegantly, we first have to introduce another important feature which has not made it in many languages (Ruby being one of the notable exceptions): code blocks. A code block in Smalltalk is just a sequence of statements in square brackets. Code block are objects (surprise) and the most important message they understand is "value" which evaluates the code.
st> x := ['Hello' printNl] ! st> x value ! 'Hello'
Since code blocks are objects, we can assign them to variables, pass them as arguments of messages, store them in collections, and so forth. Now you might guess how message passing can be used to implement control statements: The control commands become messages and the actions are passed as code blocks.
st> (2 < 3) ifTrue: x ! 'Hello' st> (2 > 3) ifTrue: x ifFalse: [ 'Ok' printNl ] ! 'Ok' st> 5 timesRepeat: x ! 'Hello' 'Hello' 'Hello' 'Hello' 'Hello'
For iterations we need blocks with arguments. They are listed as identifiers, each preceded by a colon, and separated from the block's statements by a vertical bar.
st> x := [ :i | i printNl ] ! st> x value: 15 ! 15 st> y := [ :i :j | (i*j) printNl ] ! st> y value: 2 value: 3 ! 6
Blocks with multiple arguments respond to the value message with multiple value arguments. Code blocks can be viewed as anonymous procedures. They can become arbitrarily complex with multiple statements being separated by periods. You can even use local variables which have to be declared within a pair of vertical bars.
st> x := [ :i | st> | k | st> k := 5 * i. st> 'k=' display. st> k printNl. st> (i > 5) ifTrue: [ 'big number' printNl ] st> ] ! st> x value: 10 ! k=50 'big number' st> x value: 3 ! k=15
The definition of "x" can be interpreted as the definition of a procedure similar do the following Python code.
>>> def x(i): k = 5 * i print "k=%d" % k if i > 5: print "big number" >>> x(10) k=50 big number >>> x(3) k=15
Like Smalltalk, Python allows to treat functions as objects, but it does not allow arbitrary anonymous functions (only lambda expression). On the other hand, Smalltalk's code blocks can not return values.
Now we are all set for collections and iterations. Kent Beck [BECK97]> devotes them a whole chapter in his highly recommended Smalltalk patterns book. The available methods are powerful enough to prevent you from using any explicit loop. The first example is the equivalent for a simple "for" loop (like in Pascal, not C's generalized version):
st> 1 to: 5 do: x ! 1 2 3 4 5 st> 1 to: 6 by: 2 do: [:x | x printNl ] ! 1 3 5 st> 3 to: 1 by: -1 do: [:x | x printNl ] ! 3 2 1
How is the first statement to be read? We already know that sending the "to: 5" message to the integer object "1" creates the interval from one to five. Intervals respond (like all collections) to the message "do:" with a code block as an argument. When receiving this message, the interval iterates through itself and calls the block with the current value of the iterator. The second and third statement extends this kind of loop by adding a step argument (2 and -1, respectively). Here is a more interesting example:
st> y := Set new ! st> y isEmpty printNl ! true st> y add: 'a'; add: 'b'; add: 'c' ! st> y printNl ! Set ('a' 'b' 'c' ) st> y do: x ! 'a' 'b' 'c' st> (y includes: 'a') printNl ! true st> (y includes: 'q') printNl ! false st> y remove: 'b' ! st> (y includes: 'b') printNl ! false
We create a set by sending the "new" message to the Set class (more on this below) and add three strings to the set. Note the shortcut notation when sending messages to the same object. Instead of repeating the object we can chain the messages separated by semicolons. The loop (do:) works exactly as in the previous example. The remaining statements demonstrate some more messages of sets.
All Smalltalk environments contain a rich set of collection classes including sets, lists, bags, ordered lists, arrays, and dictionaries. They all adhere to the same basic protocol which makes it easy to keep their usage patterns in mind.
st> Smalltalk at: #l put: (OrderedCollection new) ! st> l add: 'Joe'; add: 'John'; add: 'Mary' ! st> l do: [:each | each displayNl ] ! Joe John Mary st> (l at: 2) printNl ! 'John'
The "map" operation creating a new collection by applying a function to every element in the original collection corresponds to Smalltalk's "collect" message.
st> (l collect: [ :each | 'my name is ', each]) printNl ! OrderedCollection ('my name is Joe' 'my name is John' 'my name is Mary' )
If the equivalent of the "map" function exists, we also expect "filter" and "reduce". Filtering comes in two flavors, selection and rejection.
st> ((1 to: 10) select: [:each | each > 5]) printNl ! (6 7 8 9 10 ) st> ((1 to: 10) reject: [:each | each > 5]) printNl ! (1 2 3 4 5 )
Of course, rejection can be easily expressed using selection and negation (and vice versa), but having both helps to convey the intention of the program. The equivalent of "reduce" has an unusual selector "inject:into:", but otherwise behaves as expected.
st> ((1 to: 4) inject: 5 into: [:sum :each | sum + each]) printNl ! 15
A bag is an unordered collection which, in contrast to a set, allows for duplicates.
st> y := Bag new ! st> y add: 'a'; add: 'b'; add: 'b'; add: 'c' ! st> y size printNl ! 4 st> y do: [:i | i printNl] ! 'a' 'b' 'b' 'c' st> (y includes: 'b') printNl ! true st> (y occurrencesOf: 'a') printNl ! 1 st> (y occurrencesOf: 'b') printNl ! 2 st> y asSet printNl ! Set ('a' 'b' 'c' )
We have used the global dictionary "Smalltalk" already to introduce global variables. Here are a few more messages including the iteration over the key-value pairs (equivalent to Java's Map.Entry).
st> y := Dictionary new ! st> y at: 'a' put: 1; at: 'b' put: 2 ! st> y printNl ! Dictionary ( 'a'->1 'b'->2 ) st> (y at: 'a') printNl ! 1 st> (y includesKey: 'a') printNl ! true st> y associationsDo: [ :each | 'key=' display. each key display. ', value=' display. each value displayNl ] ! key=a, value=1 key=b, value=2
Smalltalk also has something like a tuple, the Array. It is a read-only, fixed length collection, just like Python's tuple, and even has a built-in syntax.
st> Smalltalk at: #x put: #(1 2 3) st> x inspect ! An instance of Array contents: [ [1]: 1 [2]: 2 [3]: 3 ]
Having covered the basics of Smalltalk syntax, it is now time to move to the heart of Smalltalk: objects and classes. It should be no surprise anymore that Smalltalk does not have any special syntax for class definitions, but relies on talking to existing classes to create new ones.
st> Object subclass: #Person instanceVariableNames: 'name age' classVariableNames: '' poolDictionaries: '' ! st> Person new printNl ! a Person
Here we create a new class called "Person" derived from Object with two attributes (or instance variables) "name" and "age". In Smalltalk speak, we tell the class object "Object" to create a subclass with the symbol #Person, and as part of the message we give Object the lists of instance variables names, class variable names, and poolDictionaries (which we won't cover here). As you can see, we can already create instances of our new class, but that's about all. We have no access to the attributes, not any Person-specific methods to call. As a next step, we can give the new class a description.
st> Person comment: 'I am representing persons with name and age' ! st> Person comment printNl ! 'I am representing persons with name and age'
The first statement set the comment using the "comment:" message, and the second one reads the comment using the "common" message and prints it. To do the same with the name of a person, we have to define our first own methods.
st> !Person methodsFor: 'setters'! st> name: aName st> name := aName st> !! st> !Person methodsFor: 'getters'! st> name st> ^name st> !! st> x := Person new ! st> x name: 'Homer' ! st> x name printNl ! 'Homer'
The notation with the exclamation marks is GNU Smalltalk specific. Normally, you create methods in a class browser where you enter the name and code in separate text fields. The argument to methodsFor is the so-called protocol the new method should belong to. Protocols are just groups of related methods. Following the protocol we find the signature of the method: the message name (or selector), and optionally the list of arguments with their names. The rest of the method definition is a sequence of statements, the body of the method. Values are returned using the caret operator. Within methods, you have access to the instance variables and to the two special variables "self" (the instance itself) and super (which we will demonstrate later).
Going through the definition of the method, there is nothing that prevents us from adding a method to an existing class. That's how we defined the methods for the Person class one after the other. As an example, we can teach every object how to say hello to the world by adding such a method to the base class Object.
st> !Object methodsFor: 'fun' st> ! st> hello st> 'Hello World' printNl st> !! st> 5 hello ! 'Hello World'
As you can imagine this opens the door for all kinds of uses (and abuses). An application can easily extend the base library. In Java applications you often find collections of utility classes containing static methods which provide additional functionality for existing objects. In Smalltalk, you would add this behaviour directly where it belongs.
Also notice that Smalltalk does not have private methods. Methods are always public and attributes are always private. If you want to mark methods as private you have to resort to some naming scheme (e.g., myDoSomething).