Objects and Messages

Smalltalk is what is known as an Object Oriented Programming Language. When you start your Dolphin image you bring into your computer's memory a sea of interconnecting and communicating objects. This chapter introduces the ideas behind what objects actually are and how you can use messages to communicate with them.

Please open a new workspace window using File/New so that you can try out the examples presented below.

What is an Object?

At the simplest level, an object is just an area of your computer's memory that contains some data bytes describing the object's state. For example, an object representing the text string, 'hello', would contain (amongst other things) the bytes:

104 101 108 108 111

Here each byte, of course, represents one character in the string. However, every object also contains additional information that allows it to "know" what kind of object it is. How this knowledge is encoded as part of the object is not relevant at this stage, but it is very important since it allows the object to determine how it should behave. For example, if we have two objects that are numbers it seems quite reasonable that we should be able to subtract one from the other. If, however, we had two text strings we would not expect to be able to perform a similar subtraction operation. The fact that the number objects "know" that they are numbers allows them to determine that subtraction is a relevant operation and also how that subtraction should be performed.

In Dolphin, there are objects that represent (amongst other things):

  • Text strings

    Numbers

    Dictionaries

    Text editors

    Windows

    Programs

  • In Smalltalk:

    Class is a very important word in Smalltalk parlance. The class of an object allows it to determine how it should behave.

    Tip: the fact that everything is an object in Smalltalk is highly significant. It makes Smalltalk a very pure object oriented programming language, much more so than other languages such as C++ or Java. This purity imparts a uniformity to the system that means that the language can be simple to learn and yet very powerful.

    When you are programming in Smalltalk, some objects can be represented directly in the language. These are called literals. Take a look at the following examples of literal objects:

    'Hello world' - a text string
    $H - the character H
    517 - an integer number
    789.45 - a floating point number
    #(1 2 3) - an array of three integers
    #show - a special object called a Symbol

    Objects communicate with Messages

    In Smalltalk, objects send messages to one another to cause things to happen. How an object responds to a particular message is determined by the class of object that it is and it is important to realize that not all objects respond successfully to all messages. The set of messages that an object can understand, and its response to these messages, determines the behavior of the object within the Smalltalk system.

    All objects know what set of messages they can understand. This set is governed by the class of the object.

    The following are typical of messages that might be sent:

    +
    -
    new
    show
    exit
    includes:
    asSortedCollection

    You can use the Smalltalk language to create objects and send messages to them. Create a new workspace window (File/New) and try the following examples. You should display the result of evaluating each line individually.

    Tip: Remember that you don't have to select the entire line to do this, just place the insert cursor somewhere on the line and type Ctrl+D (Workspace/Display it) to cause it to be evaluated and the answer displayed.

    3+4
    
    'Hello world' reverse
    
    5 factorial

    Simple Messages

    A Simple Message is one that requires no additional information or parameters apart from the message itself. Such messages must begin with a lower case letter and the remainder of the message keyword can consist of any letters or digits or the underscore (_) character. Try displaying the results of the following examples:

    5 squared.

    Here the message squared is sent to the integer object 5. All messages return an answer of some description and, in this case, it will be 25.

    One great thing about Smalltalk is its ability to handle very large numbers of effectively unlimited precision. Try this:

    200 factorial

    Here are some more message sends:

    'mary had a little lamb' asUppercase

    The message asUppercase, when sent to a string object, causes a new string to be created with the contents of the original converted to upper case letters. This new string is then answered as the result of the message.

    #(3 4 5 6 7) size

    The message size, when sent to this array object containing five integers, answers the number of elements in the array, in this case, 5.

    The object to which a message is sent is called the receiver. The message itself is sometimes called a message selector (because it selects which operation will be performed) and the result is generally referred to as the answer.

    Messages that are not understood

    We mentioned before that an object will only respond to a certain set of messages depending on what class of object it is. For example, objects that are kinds of number will hopefully handle arithmetic messages successfully. We would not, however, really expect text string objects to behave in the same way. Let's deliberately send a dubious message to a string:

    'Hi Andy' squared

    Notice that this causes a walkback window to pop-up. Dolphin uses these windows to indicate when some sort of error has occurred. The window caption indicates the basic problem, in this case, String does not understand #squared. At this stage, you could choose to enter the system debugger (by pressing the Debug button) to further diagnose the problem. However, understanding the debugger is perhaps beyond us at this point so, for the time being, get used to clicking on Terminate when you see a walkback pop up.

    Sending Messages on top of Messages

    When you send a message to a receiver, you always receive another object back as the answer. It seems perfectly reasonable that you should be able to send further messages to this answer. Try:

    20 factorial displayString

    Here, the message displayString, is sent to the result of 20 factorial. Look carefully, and you'll see that what is displayed is surrounded by single quote marks, thus indicating that the integer result has been converted to a text string representation. Perhaps we might additionally want to know how many characters are in the display string for 20 factorial:

    20 factorial displayString size

    So you can see that you can use sequences of messages to create more complex expressions.

    Binary Messages

    Sometimes, a message needs additional information to perform its work. Certain messages, called Binary Messages, require two objects; the receiver and one parameter. Try:

    3 + 4
    
    6 * 5
    
    5 squared + 6
    
    4 ** 3

    The latter ** message selector represents exponentiation, so this expression computes 4 to the power 3. Here, the integer object 4 is the receiver and 3 is the single parameter required by the ** message.

    As you can see, binary messages are often used to perform arithmetic in Smalltalk. The message selectors consist of one or more special characters that are not letters or digits. They can be used for other operations too:

    'skin' , 'diver'

    The , message is used for concatenation. In this case it is used to append the string 'diver' to the receiving object, the string 'skin'. The operation answers a new string that contains the concatenation of the two source strings. In fact, this message can be used to concatenate many similar collections of objects:

    #(1 2 3) , #(4 5 6)

    Sometimes, messages are used simply to "construct" a new object. We'll hear more about these constructor or class messages later but here is an interesting example using the binary message. @:

    (50 @ 150) class

    The @ message, when sent to an integer, is used to create a two-dimensional point object. The class message simply asks the resultant object to answer what class it is (we'll learn more about classes later). The interesting thing here is that the sequence 50@150 appears, in the Smalltalk language, to represent a two-dimensional point, but, it's important to understand that the @ symbol is not part of the language per se. Rather, it is simply a message that performs a particular operation (in this case the creation of a point object) just like any other binary message. Just for fun, try some more operations on points.

    (50 @ 150) + (25 @ 35)
    
    (640 @ 480) * 3

    Keyword Messages

    We have seen above that binary messages take a single parameter. Binary messages are special characters such as: + * - /etc. It is also possible to supply parameters to the more alphabetical style of message selectors.

    #(100 600 800 1000) at: 2.
    
    #(100 200 #( 1 2 3) 800 1000) at: 3 

    As shown above, the at: message can be used to access an object at a particular index within an array, or other indexable collection. In the second example, you see that you can have quite complex arrays which even include other arrays as their elements. This makes sense since an array can easily hold any type of object. Here are some more examples of keyword messages that take a single parameter:

    #(100 200 #( 1 2 3) 900 1000) includes: 30 squared
    
    'Hello World' occurrencesOf: $o
    
    1 to: 10

    The latter is another example of a constructor message similar to @. In this case to: sent to an integer will answer a special sort of collection known as an Interval. Intervals can be used where any other collection can be used and this one represents the range of integer numbers between one and ten. To see this better, ask it to convert itself to an array:

    (1 to: 10) asArray

    So far, we have seen how single parameters can be passed to messages ending in a :. Messages can also be created that take any number of parameters:

    #(1 2 3) at: 2 put: 'Hello'; yourself 

    We'll come back to the use of the semicolon and yourself message in the section on Cascading Messages below. For the time being concentrate on the at: and put:. These are not two separate message sends but one single one that takes two parameters. We refer to the selector for this keyword message as at:put:. As you can see, this at:put: message can be used to store objects into indexable collections such as arrays or strings. Try these:

    'Thomas,Henry,Edward' copyFrom: 8 to: 12
    
    'Thomas,Henry,Edward' midString: 5 from: 8
    
    'Hello World' at: 6 put: $-; yourself
    
    'The brown quick fox' replaceFrom: 5 to: 15 with: 'a quick brown' startingAt: 3

    Can you say what the message selector in the last example is? Yes, it's a single message that takes four parameters and has the selector, replaceFrom:to:with:startingAt:.

    Tip: I hope you can see that this way that Smalltalk has, of interspersing parameters within the body of the message selector, can lead to much more readable program code than with most other languages. If you are used to languages such as C++ or Java you may find this odd at first but I'd wager that, pretty soon, you'll come to like it!

    Comments

    Comments can be inserted into your Smalltalk code to provide additional information to help the program reader. A comment is simply text that is enclosed within double quotes.

    "This is a comment"

    Generally, Smalltalk requires fewer comments than often appear in other languages because the program text is very readable. You should only add comments where they significantly enhance the understandability of the code.

    Comments can appear anywhere but they must not breakup the fundamental items of the language such as message selectors or numbers. For example:

    365 "Days per year" * 24 "Hours per day" 

    is legal, whereas:

    36"Days per year"5 * 2"Hours per day"4

    is not. The latter will generate an error from the Dolphin Smalltalk compiler.

    Tip: users of other languages will be used to using double quote marks to denote text strings. It is obviously important that you take special note of this difference with Smalltalk: double quotes delimit comments and single quotes delimit literal text strings.

    Arithmetic

    Let's have a look at the messages for performing simple arithmetic on numbers.

    300 + 400 "Addition"
    
    3.141926 - 3 "Subtraction"
    
    365.25 * 24 "Multiplication"
    
    8766 // 24 "Integer division with truncation"
    
    8766 / 24 "Standard division"

    The interesting thing to note with Smalltalk arithmetic is that it is not necessary to match the types of numbers involved or to predict the type of the result. For example, it is perfectly reasonable to subtract an integer from a floating point number and arrive at an appropriate floating point result. You should also be familiar with the difference between the integer division and standard division messages. The former (//) will always truncate the result to the nearest whole integer below it. The latter (/) will maintain precision in the result by answering a fractional or floating point result as best it can.

    The fact that Smalltalk arithmetic is capable of switching between different number representations in order to maintain precision wherever possible is a valuable concept. Wherever possible it will try to avoid using a floating point object to hold a value since many floating point calculations are inherently inaccurate in the lower decimal places. Using objects (of class Fraction) to represent fractional values can mean that ultimate precision can be maintained where it would otherwise be lost. For example:

    6 / 18

    If this irrational result had been coerced to a floating point value (0.333333) then any subsequent calculations on this value might lose precision. However, the fractional value (1/3) remains entirely accurate. The only problem might be that you don't wish to display a fraction as the end result of your calculation since most people find floating point print outs more acceptable. The answer is simple, just forcibly convert to the appropriate representation when you want to display the result. You can try this with:

    (6 / 18) asFloat

    Order of Messages

    The order in which Smalltalk evaluates more complicated arithmetic expressions is different than you might expect, especially if you're used to other computer languages. For example, try displaying the result of:

    3 + 4 * 5

    You might expect to see a result of 23 but Smalltalk reports 35. This is because we normally think of multiplication and division operators having a higher precedence than addition or subtraction operators. We would expect the higher precedence multiplication to be performed before the lower precedence addition. This is not the case, however. In Smalltalk, all binary messages are taken to have the same precedence and will be evaluated in "first come, first served" order. This is a minor inconvenience but can be remedied by always using appropriate parentheses whenever there might be confusion. See that the following now yields the expected result:

    3 + (4 * 5)

    Smalltalk does enforce some precedence rules, though. They are very straightforward and, in most cases (apart from the above quirk concerning arithmetic precedence) they yield code that is eminently readable. The order of message evaluations in any expression is as follows:

    Try the following examples and try to predict the results:

    3 squared + 4 squared
    
    #(130 140 150 160) at: 4 - 2
    
    5 factorial gcd: 'a string' size "gcd: computes Greatest Common Divisor"
    
    3 squared + 4 squared sqrt
    
    (3 squared + 4 squared) sqrt

    Variables and Assignments

    So far, we have been evaluating individual Smalltalk expressions and displaying the results. A Smalltalk program of any real value must consist of many such expressions and, for them to be useful, it's going to be important to store the results somewhere. All computer languages use variables in which to place intermediate values so that they can be retrieved at a later time.

    In Smalltalk, a variable is a named slot or placeholder into which any object can be stored. A variable name must begin with a letter but subsequently, may consist of any letter, number or underscore characters. The following are valid variable names:

    name
    address
    dayOfWeek
    outerCount2
    day_of_week

    Although the last variable, day_of_week, is a valid name, in Smalltalk it is conventional to compose this without the underscore characters but by capitalizing subsequent words. Thus, dayOfWeek would be the preferred name.

    You can store an object into a variable using an assignment. Try the following breakdown of Pythagoras' theorem. If you wish, you can just evaluate (using Ctrl+E) the first few lines and only display the answer (using Ctrl+D) for the last line.

    a := 6.
    b := 7.
    hypotenuseSquared := a squared + b squared. "By Pythagoras"
    hypotenuse := hypotenuseSquared sqrt. 

    Perhaps you've noticed something else new here in addition to the use of the assignment (:=) operator. The Smalltalk language uses a full stop or period (.) to separate individual expressions. Now you can evaluate several expressions in one go. Try the above example again but this time select all of the text before asking to display the result.

    One thing that is different between Smalltalk variables and those used in other languages is that they are un-typed. A Smalltalk variable is just a slot capable of holding any object; you do not have to declare a variable as being able to hold only a particular type. This makes Smalltalk variables very much easier to use and, in the end, more flexible.

    Tip: the un-typed nature of Smalltalk variables is often cited as an indication that Smalltalk is not a "safe" language. Languages with strongly typed variables, that insist on a variable being declared as able to hold only a particular type of value, are seen as being safer because the compiler can perform more checks and therefore reduce the number of problems that arise at runtime.

    However, Smalltalk IS eminently type safe. Even if an incorrect class of object is placed in a variable slot, the problem will not go unnoticed during runtime testing. The object will not respond to the same set of messages that will be expected of it and will throw up a Walkback dialog when an unsuitable message is not understood.

    Dolphin Smalltalk supports three different types of variables, Workspace, Temporary, Instance and Global.

    Workspace Variables

    Workspace variables are owned by the workspace in which they are created. They are created automatically when an assignment is made to a variable beginning with a lowercase letter that has not already been declared. The variables in the above example are all workspace variables. The variable slots and the objects that they contain remain in existence until the workspace is closed. For example, try now to display:

    hypotenuse.

    This variable, created by the previous sample, is still in existence and continues to contain the result that was calculated. It will remain while the workspace window is still open and will be accessible to any other expressions that are evaluated in the same workspace. It will not, however, be visible from any other workspace that is opened.

    Temporary Variables

    It is also possible to use temporary variables that exist only during the evaluation of a series of expressions. These variables must be declared and their names must appear enclosed by vertical bar characters before the first of the series of expressions in which they are used. Temporary variable names must begin with a lowercase letter.

    You must select all of the following code to evaluate it and display the result as one unit,

    | c d theta |
    c := 8.
    d := 4.
    theta := (d / c) arcSin * 360 / (Float pi*2).

    Once the evaluation is complete all of the temporary variables are automatically destroyed.

    It is not that common to use a temporary variable when evaluating expressions within a workspace; most often you will want to use workspace variables instead. Temporaries are more often used in methods, a subject that will be dealt with in the later chapter, Classes and Methods.

    Instance Variables

    Objects have to hold their state information somewhere, and instance variables are the place. An object can contain one or more variable slots where its internal data is kept. Instance variables are private to the object in question and are not exposed to the outside world. This allows the object to keep its internal representation hidden so its users don't come to rely on it. Since it is not permissible for a user to manipulate another object's instance variables so messages become the only means to communicate with it. This encapsulation is a very important principle behind Object Oriented Programming.

    I'll show you how to create and use instance variables in the chapter on Classes and Methods.

    Global Variables

    Global variables are similar to workspace variables except that they are owned by the system rather than a particular workspace. Once you create a global it remains in existence, holding onto any contents that you may assign to it, until you explicitly remove it. A global variable name must begin with an upper case letter.

    If you execute the following code, you will be prompted as to whether you wish to create the global, MyName. Click Yes to do so.

    MyName := 'Arthur J. Rank'.
    MyName , ' was here'. 

    It is important to cleanup any globals that you may create after you have finished using them. To do this for MyName, evaluate the following (note the # sign immediately preceeding the name):

    Smalltalk removeKey: #MyName.

    You should find that you don't end up explicitly creating global variables very often. However, there is one sort of global that you will create regularly. Globals are used to hold the class objects that will form the basis of much of your Smalltalk programming. These are also introduced in Classes and Methods.

    Smalltalk maintains quite a number of global variables of which most are classes. Another, which you'll often find useful, is Transcript. This can be used to reference the System Transcript window in order to send useful text reports to it:

    10 timesRepeat: [Transcript show: 'A useful message?'; cr].

    Check the Transcript window to see the message logged to it 10 times.

    Cascading Messages

    Let's try creating a new window and manipulating it. Evaluate the following lines individually to see the incremental effect:

    window := ShellView new show.

    At this point you will have to switch back to the workspace since your new window will be brought to the front. Let's continue:

    window position: 80@80.
    window extent: 320@90.
    window backcolor: Color red.
    window caption: 'My Red Test Window'.

    We are sending a sequence of messages to the same receiver. It's quite convenient that Smalltalk offers a shorthand way of doing this, by cascading the messages to a common receiver. The following code performs a similar sequence but must be evaluated in one go since all the lines are effectively part of a single expression.

    window := ShellView new show.
    window
    	position: 80@80;
    	extent: 320@90;
    	backcolor: Color blue;
    	caption: 'My Blue Test Window'.

    Here, each of the cascading messages is separated by a semicolon. Each is sent in turn to the object held in the window variable. The lines are intended by one tab position merely as a matter of Smalltalk style to make the code more readable.

    Remember to close your red and blue windows now you've finished with them.

    So what is the result of a series of cascading messages? Well, it's the answer from the last message in sequence. Try this:

    rect := Rectangle origin: 0@0 extent: 10@10.
    rect area 

    We've created a Rectangle object with an area of 100 square units. Now let's modify the rectangle to be eight units square and re-compute the area. Once again you'll have to evaluate this as a single expression:

    rect
    	top: 1;
    	left: 1;
    	bottom: 9;
    	right: 9;
    	area

    The result displayed is that answered by the last message in sequence, area.

    Yourself

    Sometimes it is useful for a series of cascaded messages to answer, not the result of the last message, but the receiver of the messages itself. You can use the message, yourself, to do this. Compare the following two examples:

    sequence := OrderedCollection new
    	add: 1;
    	add: 4;
    	add: 9;
    	add: 16. 

    Here, the displayed result is 16, which is answered by the final add: message (it is convention that any add: message will always answer its parameter). This is probably not what we want since the actual sequence of numbers has now been lost. More likely, the following is what is expected:

    sequence := OrderedCollection new
    	add: 1;
    	add: 4;
    	add: 9;
    	add: 16;
    	yourself. 

    The message yourself always answers the receiver that it is sent to. In this case the receiver is the OrderedCollection object and so, since yourself is the last message sent in the cascaded sequence, this is what is returned as the result.

    Message Selectors are Symbols

    We mentioned previously that, in Smalltalk, everything is an object. This also goes for message selectors; they are objects too. In fact, they are examples of a certain class of object called Symbols. Symbols are similar to text string objects except that they are prepended with a # sign rather than being enclosed in single quotes. The following are all examples of literal Symbols.

    #show
    #at:put:
    #add:

    Symbols cannot contain spaces.

    The important thing about Symbols is that they are unique. This will be explained in more detailed in the chapter on Equality and Identity but the important thing to remember is that message selectors are, in fact, Symbols. For this reason, they are usually written as being prepended by a #. This will be the convention used from this point on in this guide.

    What have you learned?

    This chapter has been a long haul simply because of the fact that Smalltalk is nearly all to do with Objects and Messages. For this reason, you can now console yourself with the knowledge that you have covered around 80% of the entire Smalltalk language. So far, you have learned about:

    Perhaps most importantly you should also know that:

    As I say, we have now covered probably around 80% of the language of Smalltalk. There are very few other computer languages for which you could do this in a single chapter. The Smalltalk language is quite small because it takes a few simple concepts, such as Objects and Messages, and applies them consistently throughout. This is why you should have found the basics fairly easy to grasp so far. However, there is still a great deal to learn. Although the language itself is small, the Smalltalk system is quite large, and this is what makes it a rich and powerful environment in which to work and play. So, we've got a long way to go but, hopefully, you'll find it's worth it.

    It's probably best, at this stage, to close the workspace window you've been working with so far. This will clean up any workspace variables you've created. We'll open a new workspace at the start of each subsequent chapter.


    Click here to move on to the next chapter or here to go back to the previous chapter.