# Smalltalk/V 286: Tutorial and Programming Handbook

Auteur Inconnu

► To cite this version:

Auteur Inconnu. Smalltalk/V 286: Tutorial and Programming Handbook. 1988. hal-05188439

HAL Id: hal-05188439

https://hal.science/hal-05188439v1

Submitted on 26 Jul 2025

HAL is a multi-disciplinary open access
archive for the deposit and dissemination of sci-
entific research documents, whether they are pub-
lished or not. The documents may come from
teaching and research institutions in France or
abroad, or from public or private research centers.

L'archive ouverte pluridisciplinaire HAL, est
destinée au dépôt et à la diffusion de documents
scientifiques de niveau recherche, publiés ou non,
émanant des établissements d'enseignement et de
recherche français ou étrangers, des laboratoires
publics ou privés.

<figure><img src="image_1.png" /></figure>
Smalltalk/V 286

Tutorial
and
Programming
Handbook

digitalk inc.
The programming language Smalltalk and many of the concepts of modern user interfaces were developed in research projects at Xerox Palo Alto Research Center (PARC) over a period of several years, and culminated in Smalltalk-80. We should like to express our appreciation to the researchers in the Learning Research Group under Alan Kay and the System Concepts Laboratory under Adele Goldberg. We recognize the debt that Smalltalk/V owes to their creative efforts.

Copyright 1988 by Digitalk Inc., all rights reserved
First printing May 1988

Copying or duplicating this manual or any part thereof is a violation of the law. No part of this manual may be reproduced or transmitted in any form or by any means, electronic or mechanical, including but not limited to photocopying, without written permission from Digitalk Inc.

Digitalk Inc.
9841 Airport Boulevard
Los Angeles, California 90045

IBM is a trademark of International Business Machines Corporation; Unix is a trademark of AT&T; Smalltalk-80 is a trademark of Xerox Corporation.
# TABLE OF CONTENTS

<table><tr><td>INTRODUCTION</td><td>1</td></tr><tr><td>System Requirements</td><td>2</td></tr><tr><td>Before Starting</td><td>2</td></tr></table>

<table><tr><td>PART 1: OVERVIEW</td><td>5</td></tr></table>

<table><tr><td>Chapter 1 INTRODUCTION TO THE SMALLTALK LANGUAGE</td><td>5</td></tr><tr><td>Smalltalk's Big Ideas</td><td>5</td></tr><tr><td>Smalltalk vs Conventional Languages</td><td>6</td></tr><tr><td>The World According to Objects</td><td>11</td></tr><tr><td>Ideas Into Action</td><td>18</td></tr></table>

<table><tr><td>PART 2: SMALLTALK/V TUTORIALS</td><td>21</td></tr></table>

<table><tr><td>Chapter 2 INTRODUCTION TO THE SMALLTALK/V ENVIRONMENT</td><td>21</td></tr><tr><td>Installing Smalltalk/V</td><td>21</td></tr><tr><td>Starting Up Smalltalk/V</td><td>23</td></tr><tr><td>Exiting Smalltalk/V</td><td>24</td></tr><tr><td>Getting Around</td><td>25</td></tr><tr><td>Working with Your Mouse</td><td>25</td></tr><tr><td>Windows and Menus</td><td>26</td></tr><tr><td>Windows</td><td>26</td></tr><tr><td>Menus</td><td>28</td></tr><tr><td>Working with Windows</td><td>32</td></tr><tr><td>Quick Tour—Windows and Menus</td><td>33</td></tr><tr><td>Inside the Window Pane</td><td>37</td></tr><tr><td>Starting Out</td><td>40</td></tr><tr><td>Tutorial Files</td><td>44</td></tr></table>

<table><tr><td>Chapter 3 OBJECTS AND MESSAGES</td><td>45</td></tr><tr><td>Simple Objects</td><td>45</td></tr><tr><td>Simple Messages</td><td>46</td></tr><tr><td>Unary Messages</td><td>47</td></tr></table>
<table><tbody><tr><td>Keyword Messages</td><td>47</td></tr><tr><td>Arithmetic Messages</td><td>48</td></tr><tr><td>Binary Messages</td><td>48</td></tr><tr><td>Messages Inside of Messages</td><td>49</td></tr><tr><td>Expression Series</td><td>49</td></tr><tr><td>Cascaded Messages</td><td>50</td></tr><tr><td>Simple Loops</td><td>50</td></tr><tr><td>Objects and Messages Are Safe</td><td>51</td></tr><tr><td>Temporary Variables</td><td>51</td></tr><tr><td>Assignment Expressions</td><td>52</td></tr><tr><td>Return Expressions</td><td>52</td></tr><tr><td>Global Variables</td><td>52</td></tr><tr><td>Putting It All Together</td><td>53</td></tr><tr><td>Chapter 4 CONTROL STRUCTURES</td><td>57</td></tr><tr><td>Comparing Objects</td><td>57</td></tr><tr><td>Testing Objects</td><td>57</td></tr><tr><td>Conditional Execution</td><td>58</td></tr><tr><td>Boolean Expressions</td><td>59</td></tr><tr><td>Looping Messages</td><td>60</td></tr><tr><td>Simple Iterators</td><td>61</td></tr><tr><td>Block Arguments</td><td>61</td></tr><tr><td>Generalized Iterators</td><td>62</td></tr><tr><td>Concluding Example</td><td>64</td></tr><tr><td>Chapter 5 CLASSES AND METHODS</td><td>67</td></tr><tr><td>Classes</td><td>67</td></tr><tr><td>Methods</td><td>68</td></tr><tr><td>The Class Hierarchy Browser</td><td>69</td></tr><tr><td>The Special Variable "self"</td><td>70</td></tr><tr><td>Creating New Objects and the Special Object "nil"</td><td>70</td></tr><tr><td>Instance Variables</td><td>71</td></tr><tr><td>Recursion</td><td>71</td></tr><tr><td>Pattern Matching</td><td>72</td></tr><tr><td>Adding a Method to a Graphics Program</td><td>73</td></tr><tr><td>Class Variables</td><td>75</td></tr><tr><td>Inspectors</td><td>75</td></tr><tr><td>Chapter 6 INHERITANCE</td><td>79</td></tr><tr><td>The Class Hierarchy</td><td>79</td></tr><tr><td>Inheritance</td><td>80</td></tr><tr><td>Inheritance of Instance Variables</td><td>81</td></tr></tbody></table>
<table><tbody><tr><td>The Methods of the Animal Classes</td><td>82</td></tr><tr><td>Inheritance of Methods</td><td>83</td></tr><tr><td>The Special Variable "super"</td><td>84</td></tr><tr><td>Creating Animal Objects</td><td>84</td></tr><tr><td>Polymorphism</td><td>85</td></tr><tr><td>More General Pattern Matching</td><td>86</td></tr><tr><td>Processing Recursive Data Structures</td><td>87</td></tr><tr><td>A New Class: MonitoredArray</td><td>88</td></tr><tr><td>Class Methods</td><td>89</td></tr><tr><td>Chapter 7 STREAMS AND COLLECTIONS</td><td>91</td></tr><tr><td>Streams</td><td>91</td></tr><tr><td>Printer Stream</td><td>92</td></tr><tr><td>Collections</td><td>94</td></tr><tr><td>Generic Code</td><td>96</td></tr><tr><td>Blocks as Objects</td><td>97</td></tr><tr><td>Patterns</td><td>97</td></tr><tr><td>Computing Letter Pair Frequencies</td><td>98</td></tr><tr><td>Animals Revisited</td><td>99</td></tr><tr><td>A Network of Nodes</td><td>102</td></tr><tr><td>Chapter 8 DEBUGGING</td><td>107</td></tr><tr><td>A Document Retrieval System</td><td>107</td></tr><tr><td>How Class WordIndex Works</td><td>109</td></tr><tr><td>Debugging Class WordIndex</td><td>110</td></tr><tr><td>Hop, Skip and Jump</td><td>115</td></tr><tr><td>Chapter 9 GRAPHICS</td><td>117</td></tr><tr><td>Some Basic Concepts</td><td>117</td></tr><tr><td>The Basic Class of Graphics: BitBlt</td><td>121</td></tr><tr><td>Extension of BitBlt</td><td>130</td></tr><tr><td>Chapter 10 WINDOWS</td><td>139</td></tr><tr><td>The Prompter</td><td>139</td></tr><tr><td>Single Pane Window</td><td>140</td></tr><tr><td>Single Pane Window with More Interaction</td><td>141</td></tr><tr><td>Multi-Pane Windows</td><td>145</td></tr><tr><td>Chapter 11 OBJECT-ORIENTED DEVELOPMENT</td><td>155</td></tr><tr><td>The Smalltalk/V Application Development Cycle</td><td>155</td></tr><tr><td>Knowing When to Stop</td><td>160</td></tr></tbody></table>
<table><tbody><tr><td>Chapter 12 APPLICATION DEVELOPMENT: CASE STUDY</td><td>161</td></tr><tr><td>The Case Study: A State-Transition Perspective</td><td>161</td></tr><tr><td>The Case Study Problem as a Smalltalk/V Problem</td><td>162</td></tr><tr><td>A Window Model for the SalesCom Application</td><td>163</td></tr><tr><td>Menus Enrich the Window Model</td><td>164</td></tr><tr><td>Getting There in Half the Time: Recycling Code</td><td>165</td></tr><tr><td>Re-working the Network of Nodes</td><td>166</td></tr><tr><td>Raiding the Animal Habitat</td><td>171</td></tr><tr><td>Customers and Events: A Matter of State</td><td>176</td></tr><tr><td>Methods and Messages: Bringing the Prototype to Life</td><td>178</td></tr><tr><td>It's Getting Better All the Time: Evolutionary Development</td><td>181</td></tr><tr><td>Where to Go from Here</td><td>182</td></tr><tr><td>PART 3: SMALLTALK/V 286 REFERENCE</td><td>187</td></tr><tr><td>Chapter 13 THE SMALLTALK LANGUAGE</td><td>187</td></tr><tr><td>Objects</td><td>187</td></tr><tr><td>Classes</td><td>189</td></tr><tr><td>Messages and Methods</td><td>194</td></tr><tr><td>Chapter 14 SMALLTALK/V 286 CLASSES</td><td>203</td></tr><tr><td>Magnitudes</td><td>203</td></tr><tr><td>Streams</td><td>211</td></tr><tr><td>Interface to DOS File System</td><td>216</td></tr><tr><td>Terminal Input and Output</td><td>219</td></tr><tr><td>Collections</td><td>222</td></tr><tr><td>Window Classes</td><td>230</td></tr><tr><td>Graphic Classes</td><td>241</td></tr><tr><td>Multiprocessing Classes</td><td>256</td></tr><tr><td>Chapter 15 SMALLTALK/V 286 ENVIRONMENT</td><td>263</td></tr><tr><td>The Keypad</td><td>263</td></tr><tr><td>Active Window</td><td>264</td></tr><tr><td>Cycling</td><td>265</td></tr><tr><td>Using Menus</td><td>265</td></tr><tr><td>Manipulating Windows</td><td>266</td></tr><tr><td>Panes</td><td>268</td></tr><tr><td>Text Editor</td><td>271</td></tr><tr><td>Saving the Image</td><td>274</td></tr><tr><td>Exiting Smalltalk/V</td><td>275</td></tr><tr><td>Evaluating Smalltalk Expressions</td><td>275</td></tr></tbody></table>
<table><tbody><tr><td>The System Dictionary</td><td>277</td></tr><tr><td>Maintaining Smalltalk/V</td><td>281</td></tr><tr><td>DOS Shell</td><td>288</td></tr><tr><td>Font and Cursor Shapes</td><td>289</td></tr><tr><td>Chapter 16 SMALLTALK/V 286 STANDARD WINDOWS</td><td>293</td></tr><tr><td>Disk Browser</td><td>293</td></tr><tr><td>Class Hierarchy Browser</td><td>297</td></tr><tr><td>Class Browser</td><td>301</td></tr><tr><td>The Inspector</td><td>303</td></tr><tr><td>Debugger Windows</td><td>304</td></tr><tr><td>Method Browser</td><td>308</td></tr><tr><td>PART 4: ENCYCLOPEDIA OF CLASSES</td><td>311</td></tr><tr><td>APPENDICES</td><td>491</td></tr><tr><td>Appendix 1 SMALLTALK SYNTAX SUMMARY</td><td>491</td></tr><tr><td>How Syntax is Specified</td><td>491</td></tr><tr><td>Smalltalk Syntax</td><td>492</td></tr><tr><td>Appendix 2 PRIMITIVE METHODS</td><td>495</td></tr><tr><td>How Primitive Methods Work</td><td>495</td></tr><tr><td>Primitive Number Assignments</td><td>495</td></tr><tr><td>User Defined Primitive Methods</td><td>499</td></tr><tr><td>Appendix 3 CONFIGURING SMALLTALK/V</td><td>505</td></tr><tr><td>Memory Configuration</td><td>505</td></tr><tr><td>Hardware and BIOS Configuration</td><td>507</td></tr><tr><td>Speed vs Space</td><td>508</td></tr><tr><td>Appendix 4 METHOD INDEX</td><td>509</td></tr><tr><td>Index</td><td>547</td></tr></tbody></table>
<figure><img src="image_2.png" /></figure>
# INTRODUCTION

Welcome to Smalltalk/V and the world of object-oriented programming systems or, more often, OOPS for short. You've joined the world's largest community of Smalltalk users. Owners of Digitalk's Smalltalk/V are people, like you, who want to squeeze maximum power and performance out of their MS-DOS, OS/2 and Macintosh computers.

You're in good company. Smalltalk/V is found widely in academic and research laboratories, R&D and product development departments of Fortune 1000 corporations, systems development agencies in government as well as on home PCs for recreational and entrepreneurial pursuits. Smalltalk/V applications have been developed in the areas of simulation, expert systems, intelligent tutoring computer-based instruction, database query interfaces, computerized typesetting and integrated programming environments.

Smalltalk/V is selected by so many for such diverse applications because Smalltalk is both a powerful language—you can get a lot of activity out of a few lines of code—and a powerful program development environment—software utilities help you to reuse as many lines of pre-written code as possible and, once copied, to quickly edit and correct errors in such code for your own program.

To encourage an exploratory "design-prototype-refine" approach to application development, Smalltalk/V lets you edit and install small code modules without lengthy compile and link sessions, building a program piece by piece and seeing the results immediately. You experiment with bits and pieces of a program long before it is complete, exploring ideas, structures and algorithms as the application takes form.

Except for a small kernel in machine language, Smalltalk/V is written in Smalltalk/V. Commented source code for virtually the entire system is supplied in digestible chunks of source code which you can reuse and modify in your applications.

Smalltalk/V features pure object-oriented programming, a revolutionary approach to data abstraction, providing a new dimension in which to organize the elements of a software system. For you, this means highly reusable software, truly generic code and the opportunity to use a prototyping style of software development.

This book is intended for both people who have never used Smalltalk as well as experienced Smalltalk programmers. It's organized into five parts:

* Part 1, Overview, introduces object-oriented programming through a discussion of Smalltalk's big ideas and concludes with a comparison of Smalltalk and Pascal versions of an example program.
* Part 2, The Smalltalk/V Tutorials, is a series of tutorials that teach the Smalltalk language through examples you run in the Smalltalk/V environment.

* Part 3, The Smalltalk/V 286 Reference, is a complete specification of Smalltalk/V 286. You'll find summaries of Smalltalk's syntax and semantics, descriptions of windows and menus that make up the environment, and a rundown of the major building blocks (classes) included in the system.

* Part 4, The Encyclopedia of Classes, is a comprehensive, structured description of the classes and methods in Smalltalk/V 286.

* Appendices cover advanced features such as writing your own Smalltalk primitives and extensions in other languages and conclude with a detailed cross-referencing Method Index and Index.

## System Requirements

Smalltalk/V286 requires an IBM-PC, PS/2 or compatible, with an 80286 or 80386 processor and the following equipment:

* 1 Megabyte of RAM

* Hard disk and one diskette drive

* Monochrome or color monitor

* Graphics controller (either CGA, MCGA, EGA, VGA, Hercules, Toshiba, or AT&T)

* PC-DOS or MS-DOS, Version 2.0 or later

The following items are optional:

* Expansion to 16 Megabytes of RAM (extended memory only)

* Mouse (highly recommended, Microsoft compatible)

* Floating point co-processor (80287 or 80387)

## Before Starting

Before proceeding, please take a moment to make sure that you have the complete Smalltalk/V 286 package:

* Two diskettes labeled Image and Source

* This book

* Registration Card
The diskettes are not copy-protected. Using DOS disk copying utilities, you can make one or several backup copies, as long as they are for archival purposes so you can protect your investment.

The Smalltalk/V community is growing daily. Digitalk's user newsletter SCOOP, keeps registered Smalltalk/V users informed of programming hints, product upgrade information, bug reports, available Goodies packs, and special licensing and pricing information. To make the most of your Smalltalk/V investment, and to enable us to serve you more quickly when you need support, return the enclosed Registration Card and join the Smalltalk/V community to stay well-informed.

Sign the Registration Card and mail it to:

Digitalk Inc.
9841 Airport Boulevard
Los Angeles, California 90045
Part 1

Overview
# 1 INTRODUCTION TO THE SMALLTALK LANGUAGE

You do not have to read this chapter to get going with Smalltalk/V. One legitimate school of Smalltalk thought suggests that the best introduction to object-oriented programming is simply to jump right in—to learn Smalltalk by experience. If this notion appeals to you, proceed directly to Chapter 2. You may want to return here to supplement your experience. But there is nothing in this chapter that you have to know to understand and make effective use of Smalltalk/V.

If this were a book on driving a car, this Overview describes a bit of the "physics" behind the car's engine, drive chain and suspension—hardly prerequisite knowledge to the act of driving. But racing drivers will tell you that the more you know about how your car works, the better you can drive it—knowing how to pull maximum performance from the potential of the car's interacting component parts. If this notion appeals to you, proceed.

## Smalltalk's Big Ideas

Smalltalk grew from a few powerful ideas.

* The most important component in a computing system is the individual human user.

* Programming should be a natural extension of thinking.

* Programming should be a dynamic, evolutionary process consistent with the model of human learning activity.

* A computing environment is both a language and a productivity enhancing interface of programmer/user "power tools"—utilities to express yourself in that language and to organize and flexibly use both procedural and factual knowledge.

Smalltalk embodies these ideas in a framework for human/computer communication. At the simplest level, Smalltalk is yet another programming language like Basic, C, Pascal or Lisp. You will see in this chapter how you can write Smalltalk programs that have the "look and feel" of conventional Pascal or other familiar programming languages.

You will also see how some thirty lines of Pascal, or less than twenty lines of "Pascalese" Smalltalk, can be reduced to five lines of Smalltalk the way it is meant to be written. And that's not five lines of dense, cryptic syntaxes like C or APL allows, coding shortcuts that come back to haunt you in application maintenance and enhancement costs.
If we try to build an ideal machine that lives up to the promise of the big ideas above, we would want a computing environment that is both very hardy and forgiving. If programming is to be a natural extension of thinking and learning, the system has to take programming errors in stride—a simple coding error can't crash the system or you'd lose all incentive to use an exploratory prototyping style of application development,

Smalltalk promotes the development of *safe* systems. Smalltalk "errors" are merely objects telling you they do not understand how to do what you are asking them to do—hardly events which blow up the system. And Smalltalk's encapsulation of digestible chunks of program code with their own local data in independently active objects promotes a "divide and conquer" approach to programming problem solving. Smalltalk objects are easily inspected, duplicated, modified and, perhaps most importantly, re-used. Smalltalk lets you get on to the business of solving your problem, not writing the same code over and over.

The Tutorials will introduce you to the range of programming "power tools" standard in Smalltalk/V that help you use, re-use and modify the storehouse of Smalltalk source code which is part of the basic system. But first, it can be helpful to understand that Smalltalk is both very much like and, at the same time, very much unlike conventional programming languages.

We'll then introduce you to some of the special terminology and exciting ideas that energize object-oriented programming in Smalltalk. From there it's on to the introductory tutorial which gets you up and running and writing your first Smalltalk/V code.

## Smalltalk vs. Conventional Languages

This section presents an overview of Smalltalk/V by comparing examples of code in both Smalltalk and Pascal to help you learn Smalltalk/V more quickly. You don't have to be a Pascal programmer to benefit from the comparison as thorough explanations accompany each example.

The step-by-step code examples are followed by a complete program written in both languages which solves the same problem. We conclude by rewriting the Smalltalk version of the algorithm, taking advantage of object-oriented features to significantly reduce the amount of code required to do the same procedure.

The examples which follow present a series of statements in Pascal and Smalltalk/V. The left column shows program fragments in Pascal, while the right column shows equivalent code fragments in Smalltalk/V.
## Assignment to a Scalar Variable

a := b + c

a := b + c

These statements look the same in both Pascal and Smalltalk. The assignment operator is `:=`. Variable names have the same syntax in both languages. In the example statements, the contents of variable b are added to the contents of variable c and stored in variable a. In Pascal, the computed value is stored. In Smalltalk, assignment statements always store pointers to objects which contain the values.

## A Series of Statements/Expressions

x := 0;

x := 0.

y := 'answer';

y := 'answer'.

z := w

z := w

The statement separator is semicolon in Pascal and period in Smalltalk. Note that in both languages, the statement separator character is not used after the last statement in the series. The first statement assigns the constant zero to the variable x. The second statement assigns a literal string to the variable y. In both languages, a string is an array of characters. The third statement assigns the contents of variable w to variable z.

## A Function Call with One Argument

a := size(array)

a := array size

The function size is called with argument array and the value returned is stored in the variable a. In Smalltalk, calling a function is known as sending a message. In this case, the message size is sent to the contents of variable array.

## Function Calls with Two Arguments

x := max(x1, x2);

x := x1 max: x2.

y := sum(p, q)

y := p + q

In Pascal, the arguments to the function call are enclosed in parentheses. In Smalltalk, for a two-argument message, the arguments precede and follow the message name. Note that in Smalltalk, the standard arithmetic operations are performed via messages. In the first example, the message max: is sent to the contents of variable x1 (the first argument), with the contents of x2 as the second argument. The result returned is assigned to the variable x. In the second example, the message + is sent to the contents of variable p with the contents of variable q as the second argument, and the result returned is assigned to the variable y.
## A Function Call with Three Arguments

b := between(x, x1, x2)

b := x between: x1 and: x2

When a message has three or more arguments in Smalltalk, the name of the message is split into pieces, and a piece of the message name appears preceding each of the arguments after the first. This distribution of the message name helps to describe the message arguments. In the example, the message name is **between:and:** and the arguments are variables x, x1, and x2. This example could be used to test whether the value of x is between the values of x1 and x2, and assign the Boolean result (true or false) to the variable b.

## Subscripted Variable Access

x := a[i];

x := a at: i.

a[i + 1] := y;

a at: i + 1 put: y.

a[i + 1] := a[i]

a at: i + 1 put: (a at: i)

Pascal uses square brackets to specify subscripting, whereas Smalltalk uses **at:** and **at:put:** messages. In the first example, the value of variable i is used to index the array identified by variable a, and the value obtained is stored in variable x.

The second example shows replacing an element of an array with a new value. Note that a Pascal assignment may store into an array element, whereas in Smalltalk only scalar variables appear to the left of an assignment statement, so an **at:put:** message is used.

The third example shows accessing and changing array elements. Parentheses are used in the Smalltalk example to specify evaluation order.

## If Statements

if a < b then

a := a + 1;

a < b

ifTrue: [a := a + 1].

if atEnd(stream) then

reset(stream)

else

c := next(stream)

stream atEnd

ifTrue: [stream reset]

ifFalse: [c := stream next]

Pascal and Smalltalk provide similar capabilities for the conditional execution of a series of statements based on the result of evaluating a boolean expression. In Smalltalk, the conditional statements are enclosed in square brackets. In the first example above, the variable a will be incremented by one if the value of variable a is less than the value of variable b.
The second example illustrates conditionally executed code during file access. The file being accessed is identified by the variable *stream*. If the file is positioned at the end; the *reset* message is sent to reposition it at the beginning. Otherwise, the variable *c* is assigned the next character in the file.

## Iterative Statements

```pascal
while i < 10 do begin
  [i < 10]
  sum := sum + a[i];
  i := i + 1
end;
```

```pascal
while True: [
  sum := sum + (a at: i).
  i := i + 1].
```

```pascal
for i := 1 to 10 do
  a[i] := 0
```

```pascal
1 to: 10 do: [ :i |
  a at: i put: 0]
```

Pascal and Smalltalk provide similar capabilities for repeated execution of a series of statements. In the first example, the two statements in the loop will be executed as long as the value of the variable *i* is less than 10. In the second example, the single statement in the loop will be executed with the variable *i* taking on the values 1 through 10 in succession.

## Returning Function Results

```pascal
functionName :=
  answer;
return
```

Pascal and Smalltalk both provide for specifying the result of function (or in Smalltalk, method) evaluation. In Pascal, the function result expression is assigned to the function name, which serves as a variable for containing the result. In Smalltalk the caret (^) appears before an expression that is the method result. This causes method execution to cease and the value of the expression to be returned as the method result. In the example, the value of the variable *answer* is the function (and method) result.

## Storage Allocation and De-allocation

```pascal
new(p)          p := Array new: 5
depose(p)
```

Pascal and Smalltalk both provide for the dynamic allocation of variables (in Smalltalk terminology, objects). In the first line of the example above, both languages assign to the variable *p* a pointer to the newly allocated object. In Pascal, however, it is necessary to explicitly de-allocate objects when they are no longer needed in order to reclaim their space. This is done via the dispose function call. In Smalltalk, space reclamation (garbage
collection) is automatic and consequently, there are no language facilities for specifying object de-allocation. This simplifies programming by eliminating a potential source of error, de-allocating at the wrong time.

## A Complete Program

What follows is a complete program with Pascal code on the left, Smalltalk on the right.

program frequency;

const
  size = 80;

var
  s: string(size);
  i: integer;
  c: char;
  f: array[1..26] of integer;
  k: integer;
begin
  writeln('enter line');
  readln(s);

  for i := 1 to 26 do
    f[i] := 0;
  for i := 1 to size do
    begin
      c := asLowerCase(s[i]);
      if isLetter(c) then
        begin
          k := ord(c)
            - ord('a')
            + 1;
          f[k] := f[k] + 1
        end
    end;
  for i := 1 to 26 do
    write(f[i], ' ')
end.

  | s c f k |

s := Prompter
    prompt: 'enter line'
    default: "".

1 to: 26 do: [ :i |
  f at: i put: 0].

1 to: s size do: [ :i |

  c := (s at: i) asLowerCase.

  c isLetter
    ifTrue: [
      k := c asciiValue
      - $a asciiValue
      + 1.

  f at: k put: (f at: k) + 1
]

^f

The programs above ask the user to enter a line of text from the keyboard. It then computes the frequency of occurrence of each alphabetic character in the input text. All characters are treated as lower-case letters.
The example emphasizes the similarities of Pascal and Smalltalk syntax. The algorithm used is identical in both cases. The input characters are examined one at a time and if they are characters, the frequency counter for that letter is incremented.

None of the powerful built-in building blocks of Smalltalk were used in the above example. The example below shows the same program written using some of these built-in building blocks.

```
|sf|
s := Prompter prompt: 'enter line' default: ''.
f := Bag new.
s do: [ :c | c isLetter ifTrue: [f add: c asLowerCase]].
^f
```

A Prompter is used to get the input string from the user. A Prompter is a special type of window. An empty Bag is then created to hold the character frequencies. Bags are a type of collection that count occurrences of objects. The input string is then iterated over, and each character is examined. If the character is a letter, its lower case equivalent is added to the Bag. The resultant Bag is then returned.

Already, Smalltalk is revealing its expressive power. The considerably shorter rewrite uses a few of Smalltalk's pre-defined objects, each comes with its own highly developed behavior. In the hundred forty or so classes of Smalltalk/V object types, there are over two thousand methods you can call upon. Each new object and its methods, which you create, will be added on equal footing with the generic objects which come in Smalltalk/V.

Objects obviously have something to offer—a tremendous source of Smalltalk programmer productivity. A greater appreciation of what objects are and how they behave is in order.

## The World According to Objects

Smalltalk is built on the simple yet powerful model of "communicating objects" as shown in Figure 1.1. What could be more natural. We experience our world largely as a vast collection of discrete objects, acting and reacting in a shared environment.
<figure><img src="image_3.png" /><figcaption>Figure 1.1
Communicating Objects</figcaption></figure>

At the human social level we are a society of doctors, lawyers, beggars and thieves, etc. Although we are a population of unique individuals, we cluster in occupational groups based on the behavioral skills and knowledge we each develop and exhibit as seen below:

<figure><img src="image_4.png" /><figcaption>Figure 1.2
Human Occupational Classes</figcaption></figure>

Break a leg, call in a doctor and tell him or her about your condition. You trust the doctor's special knowledge and skills to help make you better. Self communicates with Doctor Black Box.

Want to become a lawyer? You learn the law and how to behave like a lawyer. Then as corporate counsel in response to the MegaCorp CEO's question, "What's our exposure on this new project?", your answer is couched in legal considerations while the chief financial officer reflects on fiscal impacts.
In Smalltalk's object-oriented terms, occupational abstractions like doctor, lawyer, programmer, etc., are *classes* of which we individuals are *instances*. To become a lawyer, we learn legal *methods*. Communications between individuals are comparable to Smalltalk *messages*, their content equivalent to Smalltalk * selectors* as shown in Figure 1.3. Correspondence between our perception of the world and its representation in machine terms through Smalltalk gets at the heart of Smalltalk's power.

<figure><img src="image_5.png" /><figcaption>Figure 1.3
Human and Smalltalk
Objects Communicating</figcaption></figure>

## What are objects?

A Smalltalk object is simply related pieces of code and data. The pieces of code are Smalltalk *methods*—a library of self-contained subroutines unique to each class giving each class of object its specific behaviors. An object's data structure is described by its collection of *instance variables*.

When you create a specific *instance* of a class, the initial values of the object's instance variables are assigned. The object's methods are its know-how. If we were to create a Smalltalk "car driver object", it would likely include "brake", "steer", "watch for traffic" and "shift gears" methods. Instance variables of such a car driving object would include "reaction time", "temperament" and "visual acuity" of the driver.

Related data and program pieces are encapsulated within a Smalltalk object, a communicating black box. The black box can send and receive certain messages. Message passing is the only means of importing data for local manipulation within the black box. And if an object needs something done that it does not know how to do within its own set of methods, it sends a message to another object, in effect, asking for assistance in the completion of a task.

In Smalltalk, objects communicate to objects just as lawyers talk to accountants in our occupational analogy in Figure 1.3. A professional's know-how is comparable to a Smalltalk object's collection of methods. People communicate using their know-how.
Know-how does not communicate to know-how. The lawyer's knowledge used to prepare a client's will does not include a "direct memory access" to the accountant's ability to compute financial implications of the settlement of an estate. Similarly, a Smalltalk object's methods do not call other objects' methods directly. Rather, the lawyer's methods include knowing when to send a message requesting financial services, just as the CPA knows when and how to ask for legal services.

In OOPS terms, information hiding—as this encapsulation of code and data is known in computer science—makes for highly portable, easily modifiable and safe software. Large applications may be easily maintained since objects may be updated, recompiled, tested and called immediately back into service with their new behavioral capabilities on line.

## What kinds of objects can be described?

Like their physical counterparts, Smalltalk objects have attributes and exhibit behaviors. Since everything in Smalltalk is an object—including the Smalltalk environment itself—then what you can do with the language becomes a question of what objects can be described and manipulated.

If the encapsulation of information hiding provides the means for creating objects, then a language's *data abstraction* capabilities determine what objects can be described. Marco Polo called upon his powers of data abstraction daily as he traveled to parts unknown. Things which could not be understood or named within his current world view required invention, new words for new objects.

You need the same powers of an extendible language capable of describing arbitrary data structures if you are to tailor the generic Smalltalk environment to your purposes. Smalltalk lets you create arbitrary new data structures, compound objects which can be thought of as an array whose elements can be any combination of numbers, symbols or character strings as well as another array making nested data structures possible. Where it is generally an exception or nuisance in conventional languages, creating new data structures is done routinely when you define a new class or subclass of objects in Smalltalk.

## How do objects communicate and behave?

Smalltalk objects take responsibility for their own actions, responding individually to every message. Your application may have occasion to print an integer, a floating point number, an ASCII character or a string of symbols. Since each of these elementary data types is defined as a Smalltalk class, instances of these classes come with a bundle of behavioral features built-in, its methods. Each of the elementary data types knows how to perform generally required behaviors such as print, duplicate and comparison operations.
So when it comes time to print, your Smalltalk application simply sends the near universal message `printString` to each of the variety of data type objects to be included in a report:

'This is a string' printString.

423 printString.

#(123) printString.

$A printString.

#('array of 3 'strings and' 2 'numbers') printString.

Integers, arrays and characters take care of getting themselves represented on paper. This Smalltalk characteristic of having different objects responding uniquely to the same message is known as *polymorphism*. It means you won't have to memorize a unique vocabulary for each class used in building your applications.

Because Smalltalk objects take responsibility for their own behavior, you won't have to litter your application with conditional checks through case statements to see that the proper type of function is called to operate on a piece of data. This feature saves much time and significantly reduces software maintenance costs since only affected objects need be edited and re-compiled to enhance a Smalltalk application.

Among the many reasons objects communicate, a frequent objective is to *change the state* of the receiver object. Sending a message to store a new value in a variable (a Smalltalk variable is an object that stores other objects) is an obvious example of state changing messaging. A bit more subtle is a user request to resize a window which results in a mouse-based interaction which sends new screen coordinates to the window's instance variables which store its size and location on the screen. The window's state changes upon receipt of the resize message.

Smalltalk guarantees that there will be a response by a message recipient. If an object determines that it does not know how to perform a requested behavior, it will at least answer with a "Message not understood" response message. The method which sends this response also kicks in Smalltalk's debugging utilities to help you determine and correct the failure to communicate.

So even program errors are detected and resolved within the object-oriented messaging framework of Smalltalk. This makes for a very exploratory environment in which to develop application software. A working prototype can be constructed quickly and enhancements integrated easily into the evolving system.

## How does Smalltalk organize objects and their methods?

Smalltalk organizes its classes into a hierarchy of classes and subclasses, a portion of which is shown in Figure 1.4. For example, the `Integer` class is a subclass of the `Number` class which is a subclass of `Magnitude` which is a subclass of class `Object`, the most general Smalltalk class and parent of all other classes.
<figure><img src="image_6.png" /><figcaption>Figure 1.4
Partial
Smalltalk/V
Class
Hierarchy</figcaption></figure>
Inheritance provides a mechanism for both organizing and maintaining the collection of Smalltalk object classes. Inheritance recognizes similarities among objects, capitalizing on the fact that similar objects often behave similarly.

A subclass inherits all the methods known to the parent. If you define a new customer database class to be a type of Smalltalk **SortedCollection**, your application will automatically know how to add, copy, edit, remove, print and sort customer records. You need only create methods which describe the new behaviors the customer database must exhibit beyond those of the generic **SortedCollection**.

If a selected parent's behavior is inappropriate, you simply define a method by the same name to override the parental way of doing things. For instance, to protect confidential customer information, the print method of the **CustomerRecord** class could be written to perform a password checking operation before printing. The customer record would then enforce security when asked to print itself while a less rigorously defined object simply prints itself upon receipt of the same print message.

Smalltalk's inheritance features encourage "programming to exception and modification". Without inheritance, you would have to spend a good bit of your time telling new classes of objects how to do elementary things like print. And if you later revamp the procedures for printing specific data types, you would have potentially hundreds of individual print methods to update and re-compile if it were not for inheritance.

Only slight reflection is needed to appreciate the programmer productivity potential of Smalltalk's inheritance mechanism. A little more reflection will confirm that the hierarchy of Smalltalk classes is consistent with the model of classification systems we routinely lay over our perceptions of the world.

## How do you maintain a Smalltalk world of objects?

Each Smalltalk object is an encapsulated program operating on its own local data, a little self-contained computer. The user extendable **Smalltalk/V** system comes with over two thousand pre-defined methods in over one hundred classes. With time, your personalized **Smalltalk/V** environment will contain hundreds, maybe thousands, of new objects, classes of objects and their associated methods.

If you had to explicitly manage program and data file handling for each Smalltalk object as well as act as traffic cop to the messaging activity, there would be no incentive to adopt object-oriented programming. The power of Smalltalk's natural human thinking model at the design level would be lost at code writing. **Smalltalk/V**, therefore, is designed to manage the "dirty work" of *object storage* and *memory management* for you.

Imagine the task for your hardware and operating system software if your **Smalltalk/V** directory were strewn with:
obj1.exe
obj1.prg
obj1.dat
obj2.exe
obj2.prg
obj2.dat
obj3.exe
obj3.prg
obj3.dat

... and so on for hundreds or thousands of objects if it were possible.

That's what it would take if Smalltalk's objects managed themselves independently under the conventional model of computing. Smalltalk solves this problem with a global solution, an object-oriented model for storage.

When you install Smalltalk/V at the beginning of The Tutorial, you will note two large files. These are the Smalltalk/V source and image files. The source file contains an ASCII representation of the Smalltalk source code. The image file is a computer readable snapshot of the current state of your Smalltalk/V environment, a kind of group photo preserving the state of each object.

Saving the image to permanent disk storage allows you to end a Smalltalk session, saving your objects in "suspended animation" to be revived exactly as you left them when you restart Smalltalk. In this global and efficient manner, Smalltalk takes care of storing objects.

Having an efficient approach to storage management might only serve to preserve chaos if RAM memory were to become cluttered with persistent though unused and unwanted objects, therefore taking up valuable real estate in RAM if no longer needed. Smalltalk/V lets you focus on programming while automatic memory management, sometimes unglamorously referred to as "garbage collection", maximizes RAM available for the creation of new objects and keeps accumulated "clutter" from crashing the system. Automated object storage and memory management further insure a safe environment for Smalltalk application development.

## Ideas into Action

Smalltalk/V is a prescription for an exciting programming experience ... the rare combination of an open and safe system inviting you to shape its capabilities to your needs. The first several chapters of The Tutorial introduce you to the Smalltalk/V programming environment and to the range of objects and their behavior in the Smalltalk/V system.
But this is only the beginning. Smalltalk really shows its stuff in real world application
development. The expanding collection of re-usable Smalltalk objects and their methods
when used in an evolutionary, prototyping development cycle result in quick and efficient
development of complex applications. The Tutorial concludes with an application
development example which showcases the "design-test-improve" cycle of development
encouraged by the fully object-oriented character of Smalltalk/V.
Part 2

Smalltalk/V 286
Tutorials
# 2 INTRODUCTION TO THE SMALLTALK/V 286 ENVIRONMENT

In this chapter, you will learn how to install Smalltalk/V, start up its environment, and edit and evaluate some of its most basic expressions. You'll learn how to move the cursor, use windows and enter and edit text. By the end of this chapter, you'll be familiar enough with Smalltalk/V to run the tutorials, which make up the remainder of Part 2 of this book.

If you have worked in a "windows" environment before, you may already be familiar with some ideas in this chapter. After Installing and Starting Up Smalltalk/V, you may want to take the Quick Tour and read the Starting Out sections of this chapter, then scan the rest.

If the Smalltalk/V operating environment is new to you, be sure that you understand the ideas in this chapter before moving on. You can learn more about the Smalltalk/V operating environment in Part 3: Smalltalk/V 286 Reference. Chapters 15 and 16 provide an in-depth discussion of many of the topics introduced in this chapter.

## Installing Smalltalk/V

Before you begin installing Smalltalk/V, make sure that you have made backup copies of the Source and Image diskettes included in your Smalltalk/V package.

## Copying Smalltalk/V to a Hard Disk

Copying Smalltalk/V to the hard disk involves two steps: 1) creating a subdirectory on your hard disk and 2) copying the Smalltalk/V files.

### Creating a Subdirectory

Smalltalk/V must have its own subdirectory on your hard disk. You can create a Smalltalk/V subdirectory as a child of any directory already residing on your disk. Here we are going to create a subdirectory called smaltalk, but you can name your subdirectory any name you chose.

To create the subdirectory, enter the following command at the DOS prompt then hit the return or enter key:
### C> mkdir \smaltalk

This creates a subdirectory called `smaltalk`, with the root directory as its parent on Drive "C". You may create a subdirectory from any directory on your disk. Refer to your DOS manual for instructions.

## Copying Smalltalk/V Files

To copy Smalltalk/V files to your newly created subdirectory, insert the Source diskette in Drive A. Enter the following command at the DOS prompt and hit the return or enter key:

### C> copy a:.*.* \smaltalk

This copies all files on the Drive A Source diskette to the hard disk subdirectory called `smaltalk`. After each file is copied to the hard disk, DOS reports back with a screen message. When the message "xx files copied" appears, DOS is finished copying the diskette.

Remove the Source diskette from Drive A, and replace it with the Image diskette. Repeat the procedure.

With the Smalltalk/V files copied to the hard disk, you are ready to run the installation program.

## Running the Installation Program

To install Smalltalk/V, you need only know the type of video adapter board and monitor your system is using.

### Supported Video Adapters

At time of publication, Smalltalk/V 286 supports the following:
<table><thead><tr><td>Video Adapter Type</td><td>Monitor Type</td></tr></thead><tbody><tr><td>AT&T (640 x 400)</td><td>color/mono</td></tr><tr><td>CGA (640 x 200)</td><td>color/mono</td></tr><tr><td>MCGA (640 x 480)</td><td>color/mono</td></tr><tr><td>CGA Color Graphics (640 x 350)</td><td>color</td></tr><tr><td>EGA Color Low Resolution (640 x 200)</td><td>color</td></tr><tr><td>EGA Monochrome Graphics (640 x 350)</td><td>mono</td></tr><tr><td>VGA (640 x 480)</td><td>color/mono</td></tr><tr><td>Hercules Monochrome (720 x 348)</td><td>mono</td></tr><tr><td>Toshiba T3100 (640 x 400)</td><td>mono</td></tr><tr><td>IBM 3270 (720 x 350)</td><td>mono</td></tr></tbody></table>

The installation program will present you with an up-to-date list of video adapters supported by the version of the software you have.

## The Installation Program

To install Smalltalk/V, make sure that the directory currently logged on the screen is your directory containing the Smalltalk/V files.

Now enter the following command:

### install

When you hit return or enter, the installation program displays a list of supported video adapters. Enter the number corresponding to your hardware configuration followed by the return or enter key.

The installation program performs two functions. First it records your hardware configuration in the file go in the current directory. Second, it decompresses all of the Smalltalk/V files from the distribution archives. Then the archive files are deleted.

If your hardware configuration changes, you can run the installation program again. Since the archive files have been deleted, only the first function is performed.

## Starting Up Smalltalk/V

Do not try to run Smalltalk/V unless you have already installed the Smalltalk/V program.

To start up Smalltalk/V, make sure that the current directory displayed on the screen is the directory holding Smalltalk/V programs.

Now enter the following command and hit return or enter:
v

Smalltalk/V then loads. When Smalltalk/V is ready to run, it will display the start-up screen below:

<figure><img src="image_7.png" /><figcaption>Figure 2.1
Start Up Screen</figcaption></figure>

## Exiting Smalltalk/V

To exit Smalltalk/V and return to DOS, move the cursor to the background and click the right mouse button to bring up the system menu. Select the exit smalltalk item by moving the cursor down the menu and clicking the left mouse button when the cursor is over your choice on the menu. The menu shown below will pop up on the screen:

<figure><img src="image_8.png" /><figcaption>Figure 2.2
Exit Menu</figcaption></figure>
This menu asks you whether or not to save your changes. For now, choose **forget image**; since you haven't done any programming yet, you don't have anything you need to save. Later, when you are actually programming in Smalltalk/V, you'll want to save your changes after each Smalltalk/V session.

## Getting Around

To get around the Smalltalk/V environment, you must open up and close windows, make selections from popped-up menus and move the cursor using a mouse or your keyboard.

### The Cursor

The cursor is your pointer on the screen. It tells Smalltalk/V where you are going to do something like pop-up a menu. You can move the cursor by dragging your mouse in the direction you want it to move or by using the cursor keys on the numeric keypad. When using the keypad, you can move the cursor in larger amounts by holding down the shift key while pressing down on a cursor key.

### The I-beam

The insertion point or I-beam is a special text marker that is used when editing text strings. It appears in a text window or pane and marks the spot where new text will be inserted or deleted.

## Working with Your Mouse

Using a mouse is the easiest way to get around the Smalltalk/V environment. With your mouse you can quickly move between windows and background, text and menus by dragging the mouse and clicking the correct button.

### Mouse Buttons

Your mouse has two buttons. The right button "administrates" your way around the environment. Use the right button to bring up menus for selection and for scrolling within a window pane. The left button "selects" things for Smalltalk/V to execute. Use the left button to choose specific menu items, text or text lines, and objects from a list.
## Mouse-key Equivalents

If you do not want to use a mouse, you can use your keyboard keys to get around the Smalltalk/V environment. Throughout this chapter you will find specific instructions for using the keyboard. For more keyboard instructions, refer to Chapter 15.

## Windows and Menus

Smalltalk/V is a menu-driven system. You give commands to the environment by selecting things from a menu of possible things to do. There are several different windows and menus available in the Smalltalk/V environment.

This section of the chapter introduces the windows and menus of the Smalltalk/V standard window set. You will first learn some general information about windows and menus and then you will take a Quick Tour of the main Smalltalk/V windows and menus. For more detailed reading, see Chapters 15 and 16 in Part 3 of this manual.

## Windows

A window is an object with a border, label bar, window buttons and one or more panes and pop-up menus as shown in Figure 2.3. A window can be active or non-active. Windows can be opened, closed, collapsed, resized and moved around on the screen.

<figure><img src="image_9.png" /></figure>
## Label Bar

Each window has its own label bar and menu. We will say more about menus a little further on. The window title is displayed on the label bar along with one or more small buttons, depending on which Smalltalk/V window is open. The buttons provide quick access to specific window activities.

<figure><img src="image_10.png" /><figcaption>Figure 2.4
Window Label Bar with Window Buttons</figcaption></figure>

**Close Button:** When selected, the window closes and disappears from the screen. To close the window, place the cursor onto the close button and click the left mouse button or use the numeric keypad + key.

**Zoom Button:** When selected, Smalltalk/V zooms in on the text pane so that it fills the whole screen. To select the zoom button, place the cursor onto the button and click the left mouse button or use the numeric keypad + key. To unzoom the text pane, click on the label bar and the window redraws to its original configuration. You can also use the F8 function key to zoom a text pane.

**Collapse Button:** When selected, the window collapses to show only the label bar. If the window is already collapsed, selecting this button expands the window to its original size and position on the screen. To select the collapse button, place the cursor onto the button and click the left mouse button or use the numeric keypad + key.

**Resize Button:** Select this button and the system responds with a rectangle outline for resizing the window. You can resize the window using either the mouse or keyboard.

*With a Mouse:* Be sure that the cursor is on the resize button. Press and hold down the left mouse button while you move the mouse to drag the cursor and resize the rectangle outline. Release the mouse button and the window redraws to its new size.
**With the Keypad:** With the cursor on the resize button, press and release the numeric keypad + key. Use the keyboard cursor keys to move the cursor and resize the rectangle outline. Once again, press and release the numeric keypad + key and the window redraws to its new size.

## Pane

Each window has one or more panes, depending upon which Smalltalk/V window is open. Each pane also has a menu. The pane is the workspace for programming in Smalltalk/V. Here you select items from lists using the left mouse button, insert and edit text strings using the I-beam, and select and evaluate Smalltalk/V programming code.

Each pane also has a **scroll bar** and cursor available for scrolling through all the text contained in the pane as shown in Figure 2.5. You can bring up the scroll bar by pointing the cursor onto the pane and holding down the right mouse button. We will discuss scrolling in greater detail a little further on.

<figure><img src="image_11.png" /><figcaption>Figure 2.5
Pane
with Scroll Bar
and Scroll Cursor</figcaption></figure>

## Menus

There are many menus in the Smalltalk/V environment. A menu is an object containing a list of choices relevant to a particular window, pane or other object. Menus are hidden behind windows, panes and the system screen. You can make a menu visible by popping up the menu using the mouse or keypad.
## Popping up Menus

To pop up a menu, position the cursor over the object hiding the menu. Then, click the right mouse button or press the Del key on the keyboard. The menu pops up.

## Selecting from a Menu

To select an item from a menu, move the cursor over the item you wish to select. Click the left mouse button or press the + key on the numeric keypad.

## Hiding a Menu

If you are using a mouse, move the cursor outside of the menu and click either mouse button. The menu disappears. If you are using a keypad, use the cursor keys to move the cursor out of the menu and the menu automatically disappears.

## Finding Different Menus

Smalltalk/V has many different kinds of menus some of which are shown in Figure 2.6. What kind of menu you get depends on where the cursor is when you pop it up. If the cursor is outside of every window, the system menu is popped up. When the cursor is on the window label bar, a window menu pops up. When the cursor is on a pane, a pane menu specific to that pane pops up.

<figure><img src="image_12.png" /><figcaption>Figure 2.6
Different Kinds
of Menus</figcaption></figure>
## The System Menu

The system menu lets you open new windows and perform system level functions, such as exiting Smalltalk/V or redrawing the screen.

To find this menu, place the cursor outside any window onto the screen background. Then click the right mouse button or press the Del key.

## The Window Menu

The window menu lets you manipulate the window as a whole. For example, you can resize the window by selecting *frame* from the menu. Or you can deactivate the active window and activate the window behind it by selecting *cycle* from the menu.

To quickly find this menu from any part of the window, press the *Ins* key. Or, you can place the cursor over the window label bar, and click the right mouse button or press the *Del* key.

## The Pane Menu

The pane menu lets you manipulate the contents of the window pane. For example you can evaluate selected *Smalltalk/V* code by choosing *show it* or *do it* from the pane menu. Or, you can edit selected *Smalltalk/V* code by choosing *copy*, *cut* or *paste*.

To find this menu, place the cursor anywhere inside the window pane and click the right mouse button or press the *Del* key.

## Using Menus: Running the Demonstration Program

To get you started, we've included a demonstration program, showing you how to use menus and move the cursor. To start the demonstration program, move the cursor outside of all of the windows, and pop up the menu. You should see the following screen:
<figure><img src="image_13.png" /><figcaption>Figure 2.7
Starting the Demo</figcaption></figure>

Now select the item **run demo** from the menu. (If **run demo** is not on the menu, you didn't have the cursor outside of all the windows when you popped up the menu. Try again.) When you select **run demo**, you'll see a special menu:

<figure><img src="image_14.png" /><figcaption>Figure 2.8
Running the Demo</figcaption></figure>

When you select any item from this menu, the demonstration program draws a corresponding picture. For example, select **mandala** and a mandala is drawn on the screen. Select **ball** and you'll see a little greater program complexity. As you move the cursor, the ball moves differently. The ball moves faster when you move the cursor towards the bottom of the screen, and slower when the cursor is near the top.

To leave the demo, select the **exit** item from the demo menu.
# Working with Windows

Windows provide the main interface between Smalltalk/V and you. For example, you use the **Class Hierarchy Browser** window to enter programs into the system and the **Disk Browser** window to browse and manipulate files. A window can be opened, closed, collapsed, resized and moved about on the screen.

## Opening a Window

A window must be open before it can be activated. When you first start up Smalltalk/V, the System Transcript Window is opened. To open other Smalltalk/V windows you make selections from specific menus.

For example when you select **browse disk** from the System Menu, the **Disk Browser Window** is opened. Or select **browse classes** from the same menu and the **Class Hierarchy Browser** window opens at a default size appropriate to the window type. If you want to change the size of a window, use the **resize button** on the label bar.

## Activating a Window

Although there may be many windows visible on the screen at one time, only one of them can be active. To use a window, you need to activate it.

To activate a window, move the cursor over some portion of the window and click the left mouse button or press the + key on the numeric keypad. Smalltalk/V reverses the color of the label bar to show that the window is active and ready for use.

## Deactivating a Window

You can deactivate a window by selecting another window on the screen. Click on the inactive window and its label bar reverses color to show it is active as the old window becomes deactivated.

If there are no other windows visible on the screen, move the cursor outside the window. Then click either the left mouse button or press the + key on the numeric keypad. This deactivates the window on the screen.
## Resizing a Window

An active window can be resized to your own taste or to allow you to view more than one window at a time on the screen. You can resize a window by selecting the **resize** button from the label bar and dragging the cursor to resize the window, or by selecting **frame** from the window menu and following the same procedure.

## Closing a Window

When you are finished with a window, you may close it. To close a window you can either select the **close window button** on the window label bar, or **close** from the window menu.

## Moving a Window

You can move windows. This is useful when you have collapsed or resized windows and you want to reposition them on the screen.

You can move a window in three ways. 1) Select **move** from the window menu. Then drag the cursor and its attached window outline, to its new position and click the left mouse button. The window is redrawn in its new location. 2) Place the cursor on the window label bar then press and hold the left mouse button. Drag the mouse and relocate the window on the screen. 3) Select **move** from the window menu using the **+** key on the numeric keypad. After the cursor grabs the window frame, use the cursor keys to move and reposition the window. Press the **+** key again and the window is redrawn in its new location.

## Quick-Tour-Windows and Menus

Part of the enjoyment of programming in Smalltalk/V is that you have lots of freedom to experiment. To whet your appetite, a **Quick Tour** of the main windows and menus of the Smalltalk/V environment can be found on the following pages. You can use the **Quick Tour** to preview the environment or as a roadmap in case you get lost. Detailed information on the Smalltalk/V windows and menus is found in Chapter 15 in Part 3 of this manual.
<figure><img src="image_15.png" /></figure>

**The System Menu and Transcript Window.** The Transcript Window appears when you first run Smalltalk/V. By selecting from the System Menu, you can open other windows or perform system level functions.

<figure><img src="image_16.png" /></figure>

**Disk Browser.** The Disk Browser displays and lets you edit the files on a given directory and their contents.
<figure><img src="image_17.png" /></figure>

**The Class Hierarchy Browser.** The Class Hierarchy Browser shows you the interrelationship of the classes within Smalltalk/V, and lets you edit the code for each class.

<figure><img src="image_18.png" /></figure>

**Inspector.** Inspectors let you examine and edit objects. They serve as a low-level debugging aid.
<figure><img src="image_19.png" /></figure>

**Walkback.** The Walkback window pops up automatically when errors are detected. It gives a view of the state of your program at that point.

<figure><img src="image_20.png" /></figure>

**Debugger.** The Debugger gives an expanded view of the Walkback in four panes. It is a high-level debugging aid to help you correct programming errors.
## Inside the Window Pane

Most of what you do in Smalltalk/V will involve activities inside a window pane. For many of the programs that you write, you will be able to find program code that has already been typed into Smalltalk/V. You can copy this code and edit the text strings to produce the new code you are trying to write. This saves you from having to re-invent source code each time you want to do something in Smalltalk/V.

In this section you will learn how to enter and edit text strings and how to make the most of the powerful cut, copy and paste methods found in the pane menu. For more detailed discussion of these subjects, see Chapter 15.

### Selecting Text

To do almost anything in Smalltalk/V, you must select some text to work with. This simply means marking any piece of data, such as a block of code, an expression, some text lines, etc.

Once you select text you can copy or cut it from its current location then paste it elsewhere using the relevant menu selection from the pane menu.

When working with text, you will see an I-beam in the text pane. The I-beam is the special pointer used when selecting or editing text strings. The I-beam marks the selection point for working in text. You can position the I-beam anywhere you like in the pane, even between letters in a word.

### Selecting Text Using the Mouse

To select text using the mouse, first activate the window. Move the cursor to one end of the text string. Press and hold down the left mouse button. While continuing to hold down the button, move the cursor to the other end of the text you wish to select. As you move the cursor, text is reversed. When the text you want to select is reversed, release the left mouse button. The selected text remains reversed until you deselect it. To deselect text, click the left mouse button somewhere else in the window pane.

### Selecting Text Using the Keyboard

To select text using the keyboard, make sure the window is active. Move the cursor to one end of the text you wish to select and press the `+` key on the numeric keypad. Then move the cursor to the other end of the text, and press the `-` key on the numeric keypad. The selected text is reversed. If you did not select exactly what you wanted, move the cursor and press the `-` key again.
Selecting a different piece of text is done in exactly the same way. Smalltalk/V deselects the old selected text and reverses the newly selected text strings.

## Quick Selection of Single Words and Single Lines

To quickly select a whole word in a text string, position the cursor anywhere on the word to be selected and click the left mouse button twice or press the `+` key twice in the same place—a double click. The whole word is reversed without having to drag the cursor from one end of the word to the other.

To quickly select a whole line of text, place the cursor just inside the window border to the left of the line you wish to select and click the left mouse button twice or press the `+` key twice. The whole line is selected. If you continue holding down the mouse button while you drag the mouse either up or down, line after line of text is selected until you let go of the mouse button.

## Scrolling Text

The text inside of a pane can be larger than the pane itself. Scrolling moves different text into the pane for viewing. To scroll text into the pane you can use either the mouse or the keyboard.

### Scrolling Text Using the Mouse

Make sure that the window is active. Then press the right mouse button and hold it down. The cursor changes from an arrow to a scroll cursor and the scroll bar appears on the right side of the pane. The shaded area in the scroll bar shows where the text in the window is generally located in the overall document.

Continue to hold down the button while dragging the mouse downward. The scroll cursor changes to an arrow. Drag the arrow downward outside the text pane and the text in the pane scrolls up. Drag the arrow up beyond the top of the pane and the text scrolls downward. You can use the same technique to scroll text left and right across the pane by dragging the arrow beyond the pane frame in either direction.

### Scrolling Text Using the Keyboard

To scroll text in a pane with the keyboard, first make sure that the window is active. Then press the Home and End keys to scroll the text left and right, respectively, or the PgUp and PgDn keys to scroll the text up and down, respectively. To scroll in large amounts, hold down the Shift key while pressing one of the scrolling keys.
## Scrolling to Select Large Text Areas

If the text you want to select is too long to fit in the pane at one time, there are two ways to select large areas of text. 1) Move the cursor to one end of the text string. Press and hold down the right mouse button while dragging the arrow downward as described in scrolling above. Move the cursor back into the pane when the other end of the desired text is visible. Then adjust the selection until it is just what you want before releasing the mouse button. Or 2) you can place the I-beam at one end for selection, then scroll the pane until the other end of the desired text is visible. Move the cursor over the last character to be selected. To select the text, press the **shift** key and click the left mouse button or press the **-** key on the numeric keypad. The entire section is selected.

## Inserting Text

To insert text into a pane, first activate the window. Then move the cursor to the location where you want to insert text. Click the left mouse button or press the **+** key on the numeric keypad. Notice the I-beam in the pane. The I-beam marks the insertion point for new text.

You can insert text in two ways: either by directly typing in the new text string or by using the **paste** option from the pane menu to bring in text you have selected and copied from another place. If you make a mistake, use the **backspace** key to delete your mistakes.

## Deleting Text

The easiest but slowest way to delete text is to place the I-beam after the last character you want to delete and press the **backspace** key until all the characters you wish to delete disappear from the screen. Remember that you place the I-beam by clicking the left mouse button or pressing the **+** key on the numeric keypad.

To delete whole words or strings of words, select the text to be deleted and either press the **backspace** key once, or begin typing new text, or **paste** in copied text from elsewhere. All three actions delete the selected text.

## Zooming in on a Text Pane

When **zoom** is selected using either the **zoom button** on the window **label bar** or **zoom** from the **next menu** pane menu, **Smalltalk/V** zooms in on the text window so that it fills the whole screen. You can then have a full display text view for any text entry or editing you need to do. Alternatively, you can press the **F8** function key to activate the **zoom feature**.
To return the display to normal, reselect `zoom`, or press the F8 function key again, or click the left mouse button when the cursor is inside of the window label bar.

## Starting Out

Now that you are familiar with the objects and methods of the Smalltalk/V environment, you can jump right in and get to work. As you work your way through this last section of the chapter and the tutorials that follow, you will quickly come to understand and be able to use Smalltalk/V.

## Evaluating Text

To evaluate a Smalltalk/V expression, you must first enter the text into a pane and select it. Make sure that the cursor is inside of the window, and then pop up the **pane menu**. Select either **do it** or **show it** from the menu.

When you select **do it**, Smalltalk/V executes the selected text. When you select **show it**, Smalltalk/V executes the selected text and displays the result of the expression on the screen.

For example, place the cursor into any active window pane. Type in the following expression and evaluate it:

$$3 + 4$$

When you evaluate this expression with **do it**, Smalltalk/V executes the expression but shows no results on the screen. When you evaluate this expression with **show it**, Smalltalk/V both executes the expression and shows the results. In the tutorials that follow, you will usually use the **show it** method for evaluation.

## Compilation Errors

Type the following expression into a window, select it, and then evaluate it, using the **do it** choice on the **pane menu**:

```
Turtle
    home;
    north;
    black;
    mandela: x2 diameter: 300
```

The result should be:
<figure><img src="image_21.png" /><figcaption>Figure 2.15
Compilation Error</figcaption></figure>

The compiler detected an error. The error message is inserted in front of the error and is selected. To delete the error message, press the backspace key. Now correct the expression by changing the letter x to the number 1 so that the line reads:

mandela: 12 diameter: 300

## Runtime Errors and Walkback Windows

Select the corrected expression from the above example and evaluate it. (Make sure that you select the entire expression, not just the corrected line.) The result should be:

<figure><img src="image_22.png" /><figcaption>Figure 2.16
Runtime Error</figcaption></figure>
Smalltalk/V detected a runtime error. In this case, the problem is a misspelled word: **mandela** should be **mandala**. The window notifying you of the error is called a walkback window.

To correct the error, simply activate the window with the expression and fix the misspelling. You can now select and evaluate the text again.

## Prompters

Smalltalk/V uses prompters to ask questions to which you respond by entering a string of characters. For example, the code for this prompter shows that it asks you to enter your name, and then it prints it:

Prompter prompt: 'your name ?' default: ''

To see how this works, enter the above expression into a pane, select it, and then evaluate it using the **show it** choice on the pane menu. The result should be:

<figure><img src="image_23.png" /><figcaption>Figure 2.17
Prompter</figcaption></figure>

You enter your response in the pane of the prompter. A prompter lets you enter, delete and edit the text in the usual ways, except that it accepts only a single line of text. When you press the **return** or **enter** key, the line of text is returned to Smalltalk/V as your response and the prompter is closed.
## Reusing Text

One of the nice things about Smalltalk/V is that you can edit text that you have previously evaluated, and then evaluate the expression again. For example, in the prompter example above, change 'your name?' to 'your age?' Evaluate the expression using `show it` from the pane menu.

## Browsing

Some windows, like the Disk Browser have several panes. Some of these panes have lists from which you can select items.

Pop up the system menu by placing the cursor on the screen background and clicking the left mouse button or using the + key on the numeric keypad. Select **browse disk** from the system menu. You are presented with a menu of devices. Choose one to open a Disk Browser window for that device. The pane in the upper left corner lists all of the directories on the disk. You can scroll this list both horizontally and vertically as you can with any window pane.

<figure><img src="image_24.png" /><figcaption>Figure 2.18 Disk Browser</figcaption></figure>

To select an item from the directory list, move the cursor over the desired item and click the left mouse button or press the + key on the numeric keypad. The item is then reversed. The upper right pane then displays a list of files in the directory you just selected. If you select a file in this list, the large pane at the bottom of the window displays the file contents. Part 3 of this manual explains the Disk Browser in much more detail.
Now pop up the system menu by placing the cursor on the screen background and clicking the left mouse button or using the + key on the numeric keypad. Select **browse classes** from the system menu to open the Class Hierarchy Browser window.

<figure><img src="image_25.png" /><figcaption>Figure 2.19
Class Hierarchy
Browser</figcaption></figure>

Here you can find program code for objects you may want to use in your own Smalltalk/V program. By bringing the code into any edit or text pane, you can then select it, copy it and paste it in your own program. Once the copied code is in your own program, you can edit and evaluate it until Smalltalk/V does what you want it to do.

## Tutorial Files

In the tutorials that follow, you'll be seeing many examples and programs. We've already provided these examples for you in several disk files. To save yourself the time and effort of typing in these examples, you can use the Disk Browser to access them. Open a Disk Browser for the device that contains Smalltalk/V and select the directory in which Smalltalk/V is installed.

The example files are organized by chapter. For example, the examples for Chapter 2 are in the file Chapter.2. You can see the examples at the beginning of the chapter displayed on the screen. To see the rest, just scroll the pane. You can then treat these examples by evaluating and editing them just as if you had entered them yourself.

You should now be familiar enough with the Smalltalk/V environment to proceed to the following tutorials. If you want to review any topics covered in this tutorial, you can either repeat the corresponding section of the tutorial or refer to a detailed description in Part 3, The Smalltalk/V 286 Reference.
# 3 OBJECTS AND MESSAGES

Now that you have toured the Smalltalk/V environment, you are ready to learn about the Smalltalk language itself. This chapter concentrates on the concepts of *objects* and *messages*, the basis of the Smalltalk language. You'll also be introduced to global and temporary variables.

Throughout the tutorials, you will be asked to evaluate sample pieces of code. As described in the previous chapter, you can find these examples stored in a disk file. Simply use the Disk Browser to access these files. The examples for this chapter are stored in the file *chapter.3*.

Even if you are an experienced Smalltalk programmer, use these examples. They will help you to understand the Smalltalk/V environment, as well as introduce you to some advanced Smalltalk applications. Of course, if you are new to Smalltalk, these examples will be even more valuable.

## Simple Objects

*Objects* are the basic building block of the Smalltalk language. They are analogous to pieces of data in other languages. For example,

*'this is a string'*

is a Smalltalk object, a string of characters. It's very much like a string in any other language. Here are some other simple Smalltalk objects that have counterparts in most languages:

<table><tr><td>1234</td><td>"an integer"</td></tr><tr><td>$A</td><td>"the single character A"</td></tr><tr><td>#{ 1 2 3 }</td><td>"an array of three integer objects"</td></tr></table>

Look at the last example, the array. It's an object which itself contains other objects. Let's look at some more examples:

#{ 'array' 'of' 'four' 'strings'}
#{ 'array' 'of' 5 'strings' 'and' 2 'integers'}

As you can see from the last example, all of the objects contained inside of an object do not have to be of the same type or size. Part of the power of Smalltalk comes from this capability. Consider this more complex object:

#{ 1 ( 'two' 'three' ) 4 }
This is an object (an array) with three objects inside of it. The second object in the array is another array of two strings.

## Simple Messages

Of course, an object can do nothing by itself. In Smalltalk, you send *messages* to objects to make things happen. Messages are similar to function calls in other languages. For example, look at this Smalltalk expression, composed of a single message:

### 20 factorial

This sends the message **factorial** to the object 20. To evaluate this expression, either find it in the tutorial file *chapter.3* or type it into a pane and select it. Then use the **show it** choice on the pane menu to compile and evaluate it. The result should be a very large integer:

2432902008176640000

Let's try another simple message. When you select and evaluate the following expression, the result should be the integer 15, the size of the string:

### 'now is the time' size

A message is composed of three parts: a *receiver object*, a *message selector*, and zero or more *arguments*. In the above example, the string is the receiver object, the message selector is size, and there are no arguments. Or consider:

#( 1 3 5 7 ) at: 2

In this example, the array is the receiver, at: is the message selector, and the 2 is the argument.

A message always returns a single object as its result, just like functions in most other languages. Similarly, the message selector is like the function name, and the receiver object is like the first function parameter. The above example asks for the second element of the array; the result is the integer 3.

Now try evaluating this example:

### '20' factorial

A walkback window appears. Since 20 is enclosed in quotes, it is a string, to which **factorial** makes no sense. As this example illustrates, *objects always know the messages that are appropriate for them*. Part 4 of this manual lists all of the different kinds of objects provided by Smalltalk/V, and the messages that they respond to.
To get rid of the walkback window, select the close button from the walkback window label bar by positioning the cursor over the button and clicking the left mouse button or pressing the numeric keypad + key.

## Unary Messages

Messages with no arguments are called unary messages. Try evaluating these unary messages:

```
#( 'array' 'of' 'strings' ) size
'now is the time' as UpperCase
'hello there' reversed
#( 4 'five' 6 7 ) reversed
$A asciiValue
65 asCharacter
```

## Keyword Messages

Messages with one or more arguments are called keyword messages. Try evaluating these keyword messages:

```
'now is the time' at: 6
'Hello' includes: $e
'hello' at: 1 put: $H
'The quick brown' copyFrom: 4 to: 9
```

In the last two examples, the message selectors `at:put:` and `copyFrom:to:` are divided up by the arguments. In these examples, `at:put:` and `copyFrom:to:` work with strings, but these same messages work for arrays as well:

```
#( 9 8 7 6 5 ) at: 3
#( 1 ( 2 3 ) 4 5 ) includes: #( 2 3 )
#( 1 0 4 5 ) at: 2 put: #( 2 3 )
#( 9 8 7 6 5 ) copyFrom: 1 to: 2
```

From these examples, you may have noticed another important point: different kinds of objects can respond to the same message in different ways. These arrays respond differently to the `at:put:` and `copyFrom:to:` message selectors than the strings in the previous examples.
## Arithmetic Messages

Smalltalk arithmetic looks the same as in most other languages. For example:

$$3 + 4$$

But, like other Smalltalk expressions, this is a message. The integer 3 is the receiver, + is the message selector, the integer 4 is the argument, and the integer 7 is the result. Here are some more common arithmetic messages you can evaluate.

<table><tr><td>5 * 7</td><td>"multiplication"</td></tr><tr><td>5 // 2</td><td>"integer division (truncation)"</td></tr><tr><td>4 \ 3</td><td>"integer remainder"</td></tr><tr><td>2 / 6</td><td>"rational division"</td></tr></table>

The last expression illustrates *rational arithmetic*, or the arithmetic of fractions. In Smalltalk, rational arithmetic is exact; there is no rounding or truncation. This is because Smalltalk stores the result as a numerator and a denominator (reduced to its simplest form), rather than computing an approximate real number.

Arithmetic expressions in Smalltalk also differ from most other languages in their order of evaluation. Smalltalk evaluates an arithmetic expression strictly from left to right, with no precedence among operators. For example, evaluate this expression:

$$3 + 4 * 2$$

The result is 14, not 11. You can, however, use parentheses to control evaluation order, as in this example:

$$3 + (4 * 2)$$

If your computer has a coprocessor, you can perform floating point arithmetic as well:

$$1.5 + 6.3e2$$

If a walkback window appears, your computer does not have a coprocessor. Close the walkback window and continue.

## Binary Messages

Arithmetic messages are examples of *binary messages*, messages with one argument and one or two special characters (other than digits and letters) as the selector. Binary messages are always evaluated strictly from left to right, unless you have used parentheses. For example, evaluate these non-arithmetic messages:
'hello', ' there'
#(1 2 3), # (4 5 6)

In these examples, the special character is the comma. It concatenates the argument with the receiver object.

## Messages inside of Messages

As we stated before, messages are like functions, in that they return an object. This means that anywhere an object appears in an expression, you can use a message which returns a similar kind of object. For example, evaluate:

'hello' size + 4
'now' size + # ( 1 2 3 4 ) size
# ( 1 12 24 36 ) includes: 4 factorial

The last expression above is really two messages. 4 factorial is a unary message, and is computed first. The result then becomes the argument for the includes: message. Now evaluate this more complex expression:

4 factorial between: 3 + 4 and: 'hello' size * 7

This expression is composed of five messages. The five message selectors are **factorial**, **+**, **size**, *****, and **between:and**:. As you can see, unary messages are always evaluated before binary messages, which in turn are evaluated before keyword messages. As usual, you can override this precedence by using parentheses:

'hello' at: ( # ( 5 3 1 ) at: 2 )

This expression is composed of two **at:** messages: one to an array, and one to a string. To see what happens when there are no parentheses, try:

'hello' at: # ( 5 3 1 ) at: 2

This expression is a single message with two arguments. The message selector becomes **at:at:**, clearly not what we had in mind.

## Expression Series

You can't do much with just a single expression. Here's a series of expressions, which you can evaluate as a single unit. Select the entire series before popping up the pane menu:
Turtle black.

Turtle home.

Turtle go: 100.

Turtle turn: 120.

Turtle go: 100.

Turtle turn: 120.

Turtle go: 100.

Turtle turn: 120.

Each message in the series is separated from the next by a period. We put each message on a separate line purely for appearance. The receiver of all the messages is the object *Turtle*, one of the objects supplied in the *Smalltalk/V* environment. To get a different picture, change the word **black** to **white** and evaluate the expression series again.

To clean up the screen, select *redraw screen* from the system menu.

## Cascaded Messages

A *cascaded* message is a shorthand way of writing a series of messages that are sent to the same receiver. For example, the following expression draws the same figure as the previous example (only in a different color):

Turtle
white;
home;
go: 100;
turn: 120;
go: 100;
turn: 120;
go: 100;
turn: 120

The receiver is written only once, and each message (except for the last) is terminated with a semi-colon instead of a period. The indentation, again, is optional; it simply makes the code easier to read.

## Simple Loops

We can simplify the above example by using a message that loops a specified number of times:
Turtle
black;
home.
3 timesRepeat: [ Turtle go: 100; turn: 120 ]

In this example, the argument to the `timesRepeat:` message is a block of code. Blocks of code are written as a series of messages enclosed in square brackets, [ and ]. We'd normally write the above example as:

Turtle
black;
home.
3 timesRepeat: [
Turtle
go: 100;
turn: 120]
}

This makes the cascaded message inside the block easier to see.

## Objects and Messages Are Safe

The previous series of expressions illustrates another point about objects and messages. Objects have a state; they can remember things. Messages change an object's state. The `Turtle` object remembers its position, heading, and color. The messages `black` and `white` change its color, the messages `go:` and `home` change its position, and the message `turn:` changes its heading. Let's look at another expression that emphasizes this point:

'hello' at: 1 put: 23

When you evaluate this expression, a walkback error window pops up because 23 is not a character. Since you can only change the state of the string by sending messages, the string can check the validity of the arguments. This makes Smalltalk a very safe language.

## Temporary Variables

*Temporary variables* are so called because Smalltalk discards them as soon as you are done using them. Temporary variables are declared by enclosing them in vertical bars in the first line of an expression series. Temporary variable names must start with a lower case letter, while the rest of the name can be any combination of upper and lower case letters and digits. For example, look at this short program that uses three temporary variables and a loop to compute an array of several factorials:
| temp index factorials |

factorials := #(
3 4 5 6
).

index := 1.

factorials size timesRepeat: [
    temp := factorials at: index.
    factorials at: index put: temp factorial.
    index := index + 1].

^factorials

The first line declares three temporary variables: temp, index, and factorials. A temporary variable can hold any type of object. To give it a value, you use an assignment expression.

Assignment Expressions

The above example uses four assignment expressions:

factorials := #(
3 4 5 6
).

index := 1.

temp := factorials at: index.

index := index + 1.

The first two assign objects to the temporary variables, while the last two assign the
results of messages. Since the result of a message is always a single object, they actually
assign objects to the temporary variables.

Return Expressions

The last expression in the factorial example above is:

^factorials

The caret (^) indicates that this is the value to be returned as the result of the expression
series. Such a statement beginning with a caret is called a *return expression*.

Global Variables

Smalltalk/V has many objects built into it, many of which are contained in global variables. Unlike temporary variables, Smalltalk does not automatically dispose of global variables when you are finished using them, and their use is not confined to a single set of expressions. For example, Smalltalk/V provides (among others) these three global variables:
## Turtle
Transcript
Disk

We have been using the global variable, **Turtle**. Global variables always contain a single object. For example, select **Turtle**, and then use the **show it** choice on the pane menu to see its current contents. The result is itself a single object.

Global variable names always begin with an upper case letter, with the remainder upper and lower case letters and digits. Type in the following, select it, and show it:

### Sammy

When the global variable does not currently exist, you get a menu which allows you to choose whether or not you want to create it. If you do not create the global variable, **Smalltalk/V** assumes you made an error, and displays an error message. This keeps you from accidentally creating global variables when you misspell something. As with temporary variables, you use assignment statements to assign values to global variables:

Sammy := 'Sammy Jones'

## Putting It All Together

To conclude this first tutorial, here's a graphical program that draws flowers composed of several polygons.

```FLASH
"Draw a polygon flower"
| sides length |
sides := 5.
length := 240 // sides.
Turtle
    black;
    home;
    north.
sides timesRepeat: [
    Turtle go: length.
    sides - 1 timesRepeat: [
        Turtle
            turn: 360 // sides;
            go: length]]
```

The first line is a *comment*. Comments are any string of characters enclosed in double quote marks ("comment"). **Smalltalk/V** ignores comments when it compiles the program; they simply add clarity to the code. Comments can appear anywhere in an expression series.
Evaluate the above example, and note the results. For a slightly different flower, change the number of sides. To make the polygons spread further apart, change:

Turtle go: length

to the following expression (you might want to change the color to white as well):

Turtle

up;

go: length // 2;

down;

go: length.

which produces:

<figure><img src="image_26.png" /><figcaption>Figure 3.1
Polygon Flower</figcaption></figure>

## What You've Now Learned

At this point, you should be familiar with:

* simple objects

* simple messages

* unary, keyword, and binary (including arithmetic) messages

* messages inside of other messages

* message series

* cascaded messages

* temporary and global variables

* assignment expressions

* return expressions

* comments
If you want to review any of these topics, simply refer back to the appropriate section in this chapter. Of course, Part 3 describes all of these topics in greater detail.

Here's one final point which you can think about as you proceed on to the following tutorials. As we mentioned, *Turtle*, which you have been using throughout this tutorial, is a global variable. You have been able to use this variable in a number of different situations, without knowing anything about its internal contents. This is a unique feature of Smalltalk; you can use an object simply by knowing its *external behavior*, without knowing anything of its *internal behavior*. You'll see further examples of this throughout the following tutorials.

Now that you've learned the above topics, you can proceed on to the next tutorial: control structures.
<figure><img src="image_27.png" /></figure>

<figure><img src="image_28.png" /></figure>
# 4 CONTROL STRUCTURES

In the previous tutorial, you learned some of Smalltalk's basic expressions. But like any language, Smalltalk cannot do much unless it can make decisions: evaluate a condition and perform an action based on the result, or repeat actions a specified or unspecified number of times. This chapter introduces you to Smalltalk's conditional expressions and control structures, which perform these tasks.

As always, you can access the examples for this tutorial if you do not want to type them in. Simply use the Disk Browser to retrieve the contents of the file chapter.4.

## Comparing Objects

Smalltalk compares objects by sending messages. The normal comparisons of `<`, `<=`, `=`, `>=`, `>`, and `~=` are implemented as binary messages. For example, evaluate these expressions:

```
3 < 4
#(1 2 3 4) = #(1 2 3 4)
```

All objects understand equality, `==`. Many objects also define the relational operators, or ordering messages, as in these examples:

```
'hello' <= 'goodbye'
```

Since these comparison messages are binary messages, parentheses are often needed if there are other binary messages in the expression. For example, evaluate the following expression with and without parentheses:

```
5 = (2 + 3)
```

As you have seen from evaluating these examples, comparisons return either **true** or **false**.

## Testing Objects

Many objects understand messages that let you test something about their state or condition. For example, evaluate these expressions:

```
$a isUppercase
('hello' at: 1) isVowel
7 odd
```

These messages return **true** or **false** as well.
## Conditional Execution

Most languages use `if` statements to conditionally execute a series of statements. Smalltalk uses blocks of code and messages to do the same thing. For example, look at this expression, which computes the greater of two numbers:

```
| max a b |
a := 5 squared.
b := 4 factorial.
a < b
    ifTrue: [max := b]
    ifFalse: [max := a].
^max
```

The comparison message `a < b` returns either `true` or `false`, which then becomes the receiver of the `ifTrue:ifFalse:` message. It, in turn, executes the corresponding block of code.

As you have seen, all messages return a result. The `ifTrue:ifFalse:` message, like any message, also returns a result. Evaluate the following expression:

```
3 < 4
ifTrue: ['the true block']
ifFalse: ['the false block']
```

The message `ifTrue:ifFalse:` returns the result of the last expression in the block that it executes. Let's look at another example:

```
| string index c |
string := 'Now is the time'.
index := 1.
string size timesRepeat: [
    c := string at: index.
    string
        at: index
        put:
            (c isVowel
                ifTrue: [c asUppercase]
                ifFalse: [c asLowerCase]).
    index := index + 1].
^string
```

The above example converts all consonants to lower case and all vowels to upper case, using the `c isVowel` test on each letter to find out which to do.
The other messages that execute a block of code conditionally are `ifTrue:`, `ifFalse:`, and `ifFalse:ifTrue:.`

## Boolean Expressions

The examples so far have depended on single comparison or testing messages, such as `<` or `isVowel`. But often you need to perform a compound test. To do so, you use the `and:` and `or:` messages. For example, look at the following code fragment, which tests whether a character is not a digit:

`( c < $0 or: [ c > $9 ] )`

The receiver of the `or:` message is the result of the first comparison, either `true` or `false`. The argument is a block of code whose last expression also returns `true` or `false`. The `and:` message works in the same way:

`( c >= $0 and: [ c <= $9 ] )`

To see how this is used in a complete example, evaluate this expression:

"compute the value of the first integer in a string"

`| string index answer c |`

`string := '1234 is the number'.`

`answer := 0.`

`index := 1.`

`string size timesRepeat: [`

`c := string at: index.`

`(c < $0 or: [c > $9])`

`ifTrue: [^answer].`

`answer := answer * 10`

`+ c asciiValue - $0 asciiValue.`

`index := index + 1].`

`^answer`

Notice the return expression `ifTrue: [^answer]` in the middle of the above example. This exits the expression as soon as the first non digit is encountered.

You can perform more complex tests by nesting the expressions. For example, look at the following fragment, which tests if a character is a digit or one of the letters from A-F.

`( c isDigit or: [ c >= $A and: [ c <= $F ] ] )`
Looping Messages

You've already seen one simple looping message, `timesRepeat:`. Here's a simple expression that uses another simple looping message to copy a file:

```pascal
"copy a disk file"
| input output |
input := File pathname: 'go'.
output := File pathname: 'junk'.
(input atEnd)
whileFalse: [output nextPut: input next].
input close.
output close
```

You may have noticed the message `atEnd` in the above example. This message returns `true` when there are no more characters to read from the input file stream; otherwise, it returns `false`. The input file stream is read with the next message, which returns the next character in the file. The output file stream is written with the `nextPut:` message, which writes its argument to the output.

The message `whileFalse:` is sent to a block of code with another block of code as an argument. The message repeatedly evaluates its receiver block for as long as the argument is `false`. When the receiver block evaluates to `true`, the expression closes the input and output files.

As you might expect, Smalltalk also provides a corresponding `whileTrue:` message. To see it, look at this graphical example, which uses `Turtle` to draw some polygons:

```pascal
"draw several polygons"
| sides |
sides := 3.
[sides <= 6]
whileTrue: [
    sides timesRepeat: [
        Turtle
        go: 60;
        turn: 360 // sides].
    sides := sides + 1]
```
## Simple Iterators

The Turtle example above increases the temporary variable sides from 3 to 6 by 1, and evaluates some code for each value along the way. Like most other languages, Smalltalk provides iteration statements to do this more easily. For example, here's the same expression, using one such iteration statement:

```text
"draw several polygons"
3 to: 6 do: [ :sides |
              sides timesRepeat: [
                Turtle
                  go: 60;
                  turn: 360 // sides]]
```

The iteration message is `to:do:`, which has two arguments. It takes the receiver object, 3, as the lower limit of the iteration, and uses the first argument, 6, as the upper limit. The second argument is a block of code, which itself uses a block argument, `sides`, to draw a polygon with `sides` number of sides. The iteration message assigns the values 3 thru 6 successively to the block argument, evaluating the block for each value.

The `to:do:` message uses an increment of one. But you can also specify your own increment by using the `to:by:do:` message, as in this example:

```text
"compute the sum of 1/2, 5/8, 3/4, 7/8, 1"
| sum |
sum := 0.
1/2 to: 1 by: 1/8 do: [ :i |
                       sum := sum + i].
^sum
```

The first argument, 1, is the upper limit, while the second argument, 1/8, is the increment.

## Block Arguments

As you can see from the last example, a block argument is declared in the first part of the block, preceded by a colon, : , and separated from the statements in the block by a vertical bar, | . For example, here's a block with one argument:

```text
[ :character | character isVowel ]
```

In this example, the block argument is `character`. Block arguments are a kind of temporary variable, but do not have to be declared at the beginning of the expression series.
# Generalized Iterators

Blocks with arguments allow Smalltalk to supply several generalized iteration messages: `do:`, `select:`, `reject:`, and `collect:.`

## The do: Iterator

The simplest of these is the `do:` message:

```Smalltalk
"count vowels in a string"
| vowels |
vowels := 0.
'Now is the time' do: [ :char |
    char isVowel
    ifTrue: [vowels := vowels + 1]].
^vowels
```

The `do:` iterator causes the string to iterate across itself and pass each character to the block. The above example is equivalent to the following:

```Smalltalk
"count vowels in a string"
| vowels string index |
vowels := 0.
index := 1.
string := 'Now is the time'.
[index <= string size]
whileTrue: [
    (string at: index) isVowel
    ifTrue: [vowels := vowels + 1].
    index := index + 1].
^vowels
```

The `do:` message can also iterate arrays, as in this example:

```Smalltalk
"draw several polygons"
#( 3 4 12 24 ) do: [ :sides |
    sides timesRepeat: [
        Turtle
        go: 60;
        turn: 360 // sides ]]
```

or file streams:
"Strip all carriage return characters (ascii 13)
from a disk file. Answer the number of characters
stripped."
| output stripped |
stripped := 0.
output := File pathname: 'stripped.go'.
(File pathname: 'go') do: [ :char |
    char = 13 asCharacter
    ifTrue: [stripped := stripped + 1]
    ifFalse: [output nextPut: char]].
output close.
^stripped

The above expression reformats a DOS text file for use with Unix, replacing DOS's
carriage return/line feed pair at the end of lines with Unix's single line feed.

## The select: Iterator

A more powerful iterator is the select: message:

"count the vowels in a string"
('Now is the time' select: [ :c | c isVowel ])
size

The select: message iterates across its receiver and returns all of the elements for which the argument block evaluates to true. In this case, the result is a string of all of the vowels in the original string. The message size then tells us how many elements were selected.

## The reject: Iterator

The reject: message is another generalized iterator:

"answer all digits whose factorial is
less than the digit raised to the 4th power"
#( 1 2 3 4 5 6 7 8 9 ) reject: [ :i |
    i factorial >= ( i * i * i * i ) ]

The reject: message works just as select:, but answers all elements of the receiver for
which the block of code returns false, instead of true.
## The collect: Iterator

The collect: message evaluates the block of code for each element of the receiver and answers the collection of all of the results returned by the block:

"square each element in the array"

`(1 13 7 10) collect: [:i| i * i]`

To help see the differences between select:, reject:, and collect:, evaluate the following expressions:

`(1 2 3 4 5 6 7) select: [ :c | c odd ]`

`(1 2 3 4 5 6 7) reject: [ :c | c odd ]`

`(1 2 3 4 5 6 7) collect: [ :c | c odd ]`

## Concluding Example

Our concluding example is inspired by the limitations of DOS file names. DOS limits file names to eight characters with a three character extension. Often, you need to abbreviate long names that appear inside of programs. A good algorithm is to remove lower case vowels from the original name from right to left. If this doesn't shorten it enough, you might truncate what's left to eight characters. With names already shorter than eight characters, you might want to pad the name with blanks, and not throw out any characters. Here, then, is a Smalltalk solution:

"abbreviate a long file name to 8 characters"

`| name length |`

`name := 'LongFileName'.`

`length := name size.`

`^ ( name reversed reject: [ :c |`

`c isVowel and: [`

`c islowerCase and: [`

`( length := length - 1 ) >= 8 ] ] ] )`

reversed, '

`copyFrom: 1 to: 8`

Let's examine this example in detail. The caret (^) on the fourth line tells us that the remainder of the program will return a single result. We reverse the name so that we can throw out characters from the end of the original name first. Similarly, we reverse the result of the reject: message to put the abbreviated name back in the proper order. We then append blanks to the resulting string and return the first eight characters as the answer.

Look more closely at the expression inside of the argument block to the reject: message:
```
[ :c |
c isVowel and: [
c islowerCase and: [
( length := length - 1 ) >= 8 ] ] ]
```

Remember that the reject: message eliminates only those characters for which this block evaluates to true. It's easy to see why the first two tests are `isVowel` and `islowerCase`, since they are the possible characters to eliminate. The final test is more complex:

```
( length := length - 1 ) >= 8
```

This expression must evaluate to true to delete the character, and false to keep it. The expression decrements the temporary variable length. If the length is less than 8, the character is to be kept; otherwise it is eliminated. Since we initially set `length` to the size of the string name, this expression of code returns true at most `name` size - 8 times, which is the number of characters we want to eliminate.

## What You've Now Learned

After finishing this chapter, you should be familiar with:

* comparing and testing objects

* conditional statements

* boolean expressions

* simple loops

* simple iterators

* block arguments

* the generalized iterators, `do;`, `select;`, `reject;`, and `collect`:

If you want to review any of these topics, you can either repeat the corresponding section of the tutorial, or refer to a detailed explanation in Part 3, The Smalltalk/V 286 Reference.
<figure><img src="image_29.png" /></figure>
# 5 CLASSES AND METHODS

In the preceding chapters, you have learned Smalltalk versions of techniques which are common to most programming languages. For example, you have learned how to form basic expressions, and how to use loops and conditional statements. In this chapter, you will learn some of the concepts that make Smalltalk unique: **class** and **method**. You will examine some Smalltalk/V classes and methods, and add new methods to these classes for numeric processing, pattern matching, and graphics. You'll also learn how to use **Inspectors**, windows for viewing and changing the internal variables that define the state of objects.

Beginning with this tutorial, you will make changes to the Smalltalk/V environment itself. In order to make these changes permanent (so that you can use them in later tutorials), be sure to save the image whenever you leave Smalltalk/V. That is, when you select **exit Smalltalk** from the system menu, be sure to then use the **save image** function.

As always, you can find the examples for this tutorial in the disk file *chapter.5*. Use the Disk Browser to retrieve them, if you wish.

## Classes

Problem solving using Smalltalk involves *classifying* objects according to their similarities and differences. You've already seen the external behavior of objects, by sending messages to them and observing the results. A *class* defines the behavior of similar objects by specifying their insides: the variables they contain and the methods available for responding to messages sent to them.

Every object is an *instance* (member) of a class. For example, `#(1 2 3)` and `#(sam joe)` are instances of class *Array*, whereas `north` and `south` are instances of class *String*. All objects know which class they belong to. For example, evaluate the following expressions:

`#(Francesca Jackie Marisa) class`

`'Rakesh Vijay' class`

`Turtle class`

An object's internal variables are called *instance variables*, they are themselves containers for other objects. For example, objects in class Fraction have instance variables **numerator** and **denominator**. For the object representing the fraction 1/7, the instance variable **numerator** contains the object 1 and the instance variable **denominator** contains the object 7.
# Methods

Methods are Smalltalk code, the algorithms that determine an object's behavior and performance. They are like function definitions in other languages. When a message is sent to an object, a method is evaluated, and an object returned as a result. Evaluate the following message expression:

## (1/7) numerator

When the message **numerator** is sent to the fraction 1/7, Smalltalk evaluates the method **numerator** defined in class Fraction:

### numerator

### ^numerator

The first line of the method defines the method name. (Notice that it matches the selector in the corresponding message.) The second line returns the result **numerator**, the instance variable of the receiver fraction object. As a more complex example, evaluate the following message expression:

## (2/3) * (5/7)

Sending the message `*` to the fraction 2/3 with the fraction 5/7 as the argument evaluates the method `*` in class Fraction:

## * aNumber

## ^(numerator * aNumber numerator) / (denominator * aNumber denominator)

The first line defines the method name (`*`) and the name for the argument, `aNumber`, which is used in the rest of the method to represent the argument object. The method returns a new fraction whose numerator is the product of the receiver and argument numerators, and whose denominator is the product of the receiver and argument denominators.

Notice that **numerator** and **denominator** appear both as instance variables and messages in this method. In processing the example message, the argument `aNumber` contains the fraction 5/7, while the instance variables **numerator** and **denominator** contain 2 and 3 respectively.

As you can see from this example, Smalltalk objects are abstract data types. The multiply method operates on behalf of the receiver object (2/3), whose internal variables **numerator** and **denominator** are accessible. The argument is another object (5/7). Even though it is the same class as the receiver, its internal variables are not available in this method, and so messages must be used to request the desired information. This Smalltalk feature provides complete safety from outside manipulation.
## The Class Hierarchy Browser

In using the Smalltalk/V environment up to this point, you may be wondering where you do your actual programming. To program in Smalltalk/V, you use a special window called the *Class Hierarchy Browser*. It lets you browse and change existing class and method definitions, and create new ones. Open a Class Hierarchy Browser window now by evaluating the following expression:

ClassHierarchyBrowser new openOn: (Array with: Integer
with: Fraction with: String with: DemoClass)

A new window will appear on the screen. Remember that you can resize or move this window as you learned in Chapter 2. A Class Hierarchy Browser window is now available for the classes Integer, Fraction, String and DemoClass, as you can see from the top left pane. Select the entry for class Fraction; you'll see the following window:

<figure><img src="image_30.png" /><figcaption>Figure 5.1
Class Hierarchy
Browser</figcaption></figure>

The top right pane shows the methods defined for class *Fraction*, the selected class. The bottom pane shows the *class definition message* for class *Fraction*. The class definition message shows the characteristics that make up a class. Notice the *instanceVariableNames*: argument; it's a string specifying that the instance variable names are *numerator* and *denominator*. You'll learn about this message's other arguments later in the chapter.

Select the method `*` in the top right pane; the source code for the method appears in the bottom pane. This pane is a text editor which you can use to change existing methods and create new ones. Try selecting other methods, and look at the source code.
## The Special Variable "self"

Now let's add the following new method to class Fraction:

### fraction

"Answer the receiver minus its integral part."
^self - self truncated

This method returns a fraction less than one, the receiver of the message minus the integral part of the receiver. The method contains the word self, a special variable representing the object which is the receiver of the fraction message. Add the method to class Fraction using the following steps:

* Pop up the pane menu in the top right pane and select new method.

* You'll see a prototype method in the bottom pane. Replace it with the source code for the fraction method defined above.

* Pop up the pane menu in the bottom pane and select save.

Smalltalk/V compiles the new method and installs it in class Fraction. Try it out by evaluating the following messages:

(22/7) fraction

(2/3) fraction

## Creating New Objects and the Special Object "nil"

You have seen several messages which create new objects, such as:

'bigger', ' string'
1/3

Classes are also objects, and so can be used in message expressions. A common way to create a new object is to send a message to its class. For example, evaluate the following messages:

Array new: 10

Array new

Pen new

Date today

Time now

The first message creates an array with 10 elements, all initialized to the object nil. The object nil is the sole instance of class UndefinedObject; it is assigned to the instance variables of all new objects. This means that unless an object assigns a value to its instance variables, they contain nil. The second message, on the other hand, creates an array with no elements at all.
The third message creates a pen, an instance of class Pen; if you show it, it displays itself as "a Pen". This is the default way for an object to display itself. (In Chapter 7, you'll learn how to include more information when an instance displays itself.)

The final two messages create an instance of class Date (representing the current date), and an instance of class Time (representing the current time), respectively.

## Instance Variables

Objects can contain both *named* and *indexed* instance variables. Named instance variables are accessed by name, as with *numerator* for fraction objects. Indexed instance variables are identified by integers beginning with 1. They are always accessed via messages, such as:

* 'location' at: 2

* 'parts' at: 5 put: $y

An object's class specifies the named instance variables, and whether or not indexed instance variables can be used in its instances. The number of named instance variables is fixed for all instances of the class. The number of indexed instance variables is defined when you create the object, and may differ among instances of a class. For example, the two strings above have eight and five indexed instance variables, respectively.

For a complete description of how to specify class information, refer to Part 3, The Smalltalk/V 286 Reference.

## Recursion

A powerful programming technique is *recursion*. Recursion is often used when an algorithm or data structure is defined in terms of itself. In Chapter 3, you saw examples using the *factorial* message. Let's look at the *factorial* method, defined in class Integer:

factorial

```
    "Answer the factorial of the receiver."
self > 1
    ifTrue: [^ (self - 1) factorial * self].
self < 0
    ifTrue: [^ self error: 'negative factorial'].
^1
```

The *factorial* method multiplies the receiver by the factorial of the quantity, receiver minus one. If the receiver is less than or equal to one, the answer is one. As in this example, a recursive solution is often a straightforward translation of a mathematical definition into a Smalltalk method.
Evaluate the following expression, which sends the **factorial** message to each of the elements of an array and returns the answers in a new array:

```groovy
#(0 1 2 3 4 10 15 20) collect: [ :n | n factorial ]
```

As another example of recursion using integers, consider how to compute Fibonacci numbers. A Fibonacci number is a statistical function used in many applications. The nth Fibonacci number for n greater than 2 is defined to be the sum of the Fibonacci numbers for n - 1 and n - 2. (The Fibonacci number for n less than 3 is one.) Use the Class Hierarchy Browser to add the following method to class Integer (Note that you can copy the method from the tutorial file chapter.5 and paste it over the new method template in the bottom pane of the Class Hierarchy Browser):

### fibonacci

"Answer the nth fibonacci number, where n is the receiver."

```groovy
^self <3
ifTrue: [1]
ifFalse: [
    (self - 1) fibonacci + (self - 2) fibonacci
]
```

Notice that the **fibonacci** method returns its result differently from the **factorial** method. Instead of using the caret (`^`) in multiple places, as in **factorial**, **fibonacci** uses a single caret to return the result of the `ifTrue`:`ifFalse:` message. That result is the result of either of the two blocks, depending on the value of the `<message`. Test the **fibonacci** method by evaluating the following message:

```groovy
#(1 2 3 4 5 6 7 10 20) collect: [ :m | m fibonacci ]
```

## Pattern Matching

The following example illustrates simple pattern matching applied to strings. (Later, you'll see how Smalltalk's inheritance allows this same method to do pattern matching for several other classes as well.) Add the method `indexOfString`: to class `String` using the Class Hierarchy Browser:
`indexOfString: aString`
"Answer the index position of the first occurrence of aString in the receiver. If no such element is found, answer zero."
`| index1 index2 limit1 limit2 |`
`limit2 := aString size.`
`limit1 := self size - limit2 + 1.`
`index1 := 1.`
`[index1 <= limit1]`
`whileTrue: [`
`index2 := 1.`
`[index2 <= limit2`
`and: [(self at: index1 + index2 - 1)
= (aString at: index2)]]`
`whileTrue: [index2 := index2 + 1].`
`index2 > limit2`
`ifTrue: [^index1].`
`index1 := index1 + 1].`
`^0`

This method starts at the beginning of the receiver string and searches for the first occurrence of the argument string. It returns either the index of the first character in the receiver's matching substring, or 0 if there are no matches. The method contains two nested `whileTrue:` loops. The outer loop proceeds through the characters of the receiver. Beginning at each character reached in the outer loop, the inner loop compares the characters of the argument to corresponding characters of the receiver.

Test the `indexOfString:` method by evaluating each of the following messages:

'abcdebcd' `indexOfString: 'ebg'`
'abcdebcd' `indexOfString: 'bcd'`
'abcdebcd' `indexOfString: 'c'`
'abcdebcd' `indexOfString: 'abcdebcd'`
'abcdebcd' `indexOfString: ''`

## Adding a Method to a Graphics Program

In Chapter 3 you used a series of expressions to draw a polygon flower on your display. The next example packages those expressions into a method, and extends the graphics demo program so that you can choose the polygon flower from the demo program's menu.
To create the new method, add the following to class DemoClass using the Class Hierarchy Browser:

```python
polyFlower
    "Draw a polygon flower"
    | sides length |
    Display white: rectangle.
    sides := Prompter
        prompt: 'Number of sides?'
        defaultExpression: '30'.
    length := 240 // sides.
    pen
        home;
        north.
    sides timesRepeat: [
        pen
            up;
            go: length // 2;
            down;
            go: length.
    sides - 1 timesRepeat: [
        pen
            turn: 360 // sides;
            go: length]]
```

The `polyFlower` method differs from the polygon flower expressions used earlier in two ways. Firstly, this method uses a prompter window, rather than a constant, to determine the number of sides. Secondly, this method uses the instance variable `pen`, rather than the global variable `Turtle`, to draw the polygon flower.

To add "poly flower" as a choice in the graphics demo menu, extend the `demoMenu` method in class `DemoClass`, as follows:

```python
demoMenu
    ^Menu
        labels: ('exit\poly flower\walking line\',
                   'dragon\mandala\mandalas\pentagons\',
                   'spirals\ellipses\bouncing ball') withCrs
        lines: #{1 5 8}
        selectors: #{exit polyFlower walkLine dragon mandala
                    multiMandala multiPentagon multiSpiral multiEllipse bounceBall}
```

Replace the `demoMenu` method in class `DemoClass` with the version above. Now try the extended demo program by selecting `run demo` from the system menu.
## Class Variables

Class variables are global variables accessible to all instances of a class. They are used to share data within a class. Class variables begin with a capital letter.

Let's add a class variable to DemoClass to count the number of times we perform the mandala graphics demo method. First, define the new class variable using the Class Hierarchy Browser. Select DemoClass, then edit the class definition to have the name MandalaCount following VariableCount in the classVariableNames: argument. Then pop up the pane menu and select save. This creates the class variable and recompiles DemoClass. Now add the following instance method to DemoClass:

```
mandalaCount
^MandalaCount
```

Then add the following code at the end of the mandala method in DemoClass:

```
MandalaCount isNil
ifTrue: [MandalaCount := 1]
ifFalse: [MandalaCount := MandalaCount + 1]
```

To see how many times mandala has been drawn, evaluate the following expression before and after running the demo program:

```
DemoClass new mandalaCount
```

## Inspectors

An Inspector is a window which allows you to view and change an object's instance variables. Evaluate the following expression to create an inspector window on an array:

```
| a |
a := #(
  1 2 sam 'joe' (4 5)).
a at: 2 put: 3 / 4.
a inspect
```

The inspector on an array looks as follows:
<figure><img src="image_31.png" /><figcaption>Figure 5.2
Inspector</figcaption></figure>

The left pane of the inspector shows the names of the named instance variables and the numbers of the indexed instance variables. You select a name or number in the left pane to see the object contained in that variable in the right pane. You can create an inspector on the contents of an instance variable by selecting the variable, popping up the pane menu and selecting **inspect** or by clicking on a selected variable. Try this by inspecting the second instance variable of the array which contains a fraction object (3/4).

The fraction object has instance variables named **numerator** and **denominator** with values 3 and 4, respectively. Let's try changing the **denominator** variable. First select **denominator**, and then go to the right pane and use the text editor to replace 4 with 100. Then pop up the pane menu and select **save**. The instance variable is now changed. Just to make sure, close the **fraction inspector window**, return to the **array inspector window**, and select **self**. There it is; the fraction has been changed to 3/100.

## What You've Now Learned

By the end of this tutorial, you should now be familiar with:

* classes and methods

* the Class Hierarchy Browser

* the special variable **self**

* creating new objects

* the special object **nil**

* named instance variables

* indexed instance variables

* recursion

* pattern matching

* adding new methods
* class variables

* inspectors

As always, if you need to review any of these topics, you can repeat that section of the tutorial, or refer to a complete description in Part 3, *The Smalltalk/V 286 Reference*.

If you are going to exit the Smalltalk/V environment before proceeding to the next tutorial, be sure to save the image.
<figure><img src="image_32.png" /></figure>
# 6 INHERITANCE

This chapter presents Smalltalk's *class hierarchy* and the concept of *inheritance*. You'll see inheritance through an example of animal classification and see how to generalize the pattern matching method you saw in Chapter 5. You'll also see how to add new classes to *Smalltalk/V*, using the Class Hierarchy Browser. And finally, you'll be introduced to the Smalltalk concept of *polymorphism*, and how to process recursive data structures.

As always, the examples for this section are stored on a disk file, *chapter.6*. You can use the Disk Browser to load these files if you do not want to type the examples.

You'll also add new classes and methods to your environment during this lesson, so be sure to save the image when you exit the environment.

## The Class Hierarchy

Much of Smalltalk's power comes from arranging its classes in a hierarchy. Each class has an immediate superclass and possibly one or more subclasses, with class *Object* at the top of the hierarchy. You're already familiar with this same system in biology, which arranges living organisms in classes, based on characteristics common to each class. Classes higher in the hierarchy represent more general characteristics, while classes lower in the hierarchy represent more specific characteristics. For example, *fish* and *tree* are more abstract than *halibut* and *maple*.

In Chapter 5, you saw how Smalltalk organizes its code (methods) by class. In this and later chapters, you will see how you can develop generic problem solutions using abstract classes, and then develop more application-specific solutions which "specialize" the general solution by adding a small amount of code in subclasses.

Close the Class Hierarchy Browser window opened in Chapter 5, pop up the system menu, and select the *browse classes* choice. This opens a new Class Hierarchy Browser on the entire class hierarchy. Select class *Boolean* in the class list pane. Then pop up the pane menu and select the *hide/show* menu choice or click on *Boolean* a second time. Now select class *True*, which shows you the following window:
<figure><img src="image_33.png" /><figcaption>Figure 6.1
Class True</figcaption></figure>

Notice that the classes in the class list pane are indented. The indentations show the class hierarchy. Each class is a superclass of the classes indented below it. As you can see, **Object** is the superclass of all classes, and **Boolean** is the superclass of **True** and **False**.

A class with "..." following its name has subclasses that are not displayed. When you first open the Class Hierarchy Browser, it displays only the first level subclasses of class **Object**. This keeps the pane from becoming too cluttered. To display a class' hidden subclasses (or hide a class' displayed subclasses), simply use *hide/show*, as you did above to show **True** and **False** in class **Boolean** or double click on the class name. Try hiding the subclasses of class **Boolean**.

## Inheritance

*Inheritance* is the Smalltalk capability which allows you to re-use software by specializing already existing general solutions. To see this, we'll define a new class hierarchy of *animals*.

<figure><img src="image_34.png" /><figcaption>Figure 6.2
Animal Hierarchy</figcaption></figure>
You'll see these same classes again in the following chapters to illustrate collections, graphics and window applications. The class **Animal** is a subclass of class **Object**. In turn, classes **Bird** and **Mammal** are subclasses of class **Animal**. Finally, classes **Parrot** and **Penguin** are subclasses of class **Bird**, and classes **Dog** and **Whale** are subclasses of class **Mammal**.

Whenever you define a new class, you also declare its instance variables. The following shows in parentheses the instance variables defined for each class in the animal hierarchy:

*   **Animal** (name, knowledge, habitat, topSpeed, color, picture)
    *   **Bird** (flying)
        *   **Parrot** (vocabulary)
        *   **Penguin** ()
    *   **Mammal** ()
        *   **Dog** (barksAlot)
        *   **Whale** ()

## Inheritance of Instance Variables

In this chapter, we'll use the instance variables **name**, **vocabulary** and **barksAlot**. (You'll see the others used in subsequent chapters.) The instance variable **name** contains a string representing the animal's name, **vocabulary** contains a string of all words known by a parrot, and **barksAlot** contains either **true** or **false**, depending upon how much a dog barks.

An object inherits all the instance variables defined in its superclasses in addition to containing the ones defined in its own class. For example, parrots, penguins, dogs and whales each contain the following instance variables:

*   **Parrot**
    *   name, knowledge, habitat, topSpeed, color, picture, flying, vocabulary

*   **Penguin**
    *   name, knowledge, habitat, topSpeed, color, picture, flying

*   **Dog**
    *   name, knowledge, habitat, topSpeed, color, picture, barksAlot

*   **Whale**
    *   name, knowledge, habitat, topSpeed, color, picture

Normally, you create new classes using the Class Hierarchy Browser. (You'll see how to do this later in this chapter.) Since we have several classes and methods to define for the animal class hierarchy, however, we've simplified the procedure for you by putting them in a file. To add the animal classes to your **Smalltalk/V** environment, install the file by evaluating the following expression:
(File pathName: 'animal6.st') fileIn

Now, in order to see these new classes with the Class Hierarchy Browser, activate the Class Hierarchy Browser window, pop up the class list pane menu, and select the update function. Now all the animal classes are visible. If they are not, use the hide/show selection from the class list pane menu. By selecting its classes and methods, you can now browse the animal hierarchy.

## The Methods of the Animal Classes

As you can see from the Class Hierarchy Browser, the methods you have just included for class Animal are as follows:

**answer: aString**

"Display a message for the receiver animal on the System Transcript window, consisting of the animal's class name and name preceding aString."

**Transcript nextPutAll:**

self class name, ' ', name, ': ', aString; cr

**name: aString**

"Change the receiver animal's name to aString." name := aString

**talk**

"Display a message that the receiver can't talk." self answer: 'I can't talk'

Similarly, the methods for class Parrot are:

**talk**

"Display a message containing the receiver parrot's vocabulary." self answer: vocabulary

**vocabulary: aString**

"Change the receiver parrot's vocabulary to aString." vocabulary := aString

And finally, the methods for class Dog are:
**bark**

"Have the receiver dog bark by ringing the bell
and displaying a bark message."

Terminal bell.

**barksAlot**

ifTrue: [self answer: 'Bow Wow, Bow Wow, Bow Wow!']

ifFalse: [self answer: 'Woof']

**beNoisy**

"Change the status of the receiver dog to noisy."

barksAlot := true.

self answer: 'I'll bark a lot'

**beQuiet**

"Change the status of the receiver dog to quiet."

barksAlot := false.

self answer: 'I won't bark much'

**talk**

"Have the receiver dog talk by barking unless
barksAlot is nil, in which case the superclass
can decide how to talk."

**barksAlot isNil**

ifTrue: [super talk]

ifFalse: [self bark]

We didn't define any methods for classes Penguin and Whale. However, these classes
do inherit the methods of class Animal, so we can create whale and penguin objects and
send messages to them, as we do later in this chapter.

Inheritance of Methods

Like instance variables, methods are also inherited. When a message is sent to an object, Smalltalk looks for the corresponding method defined in the object's class. If it finds the method, Smalltalk performs it. If it doesn't find the method, however, Smalltalk repeats the procedure in the object's superclass. This process continues all the way to class Object. If no method is found in any superclass, a walkback window pops up to display the error.

For example, look at the name: method defined in class Animal. Since this method is not
defined in any of Animal's subclasses, the name: method in class Animal is evaluated
whenever a name: message is sent to instances of classes Dog, Parrot, Penguin, or
Whale.
As another example, look at the **talk** method in the animal classes. Classes **Penguin** and **Whale** inherit **talk** from class **Animal**, whereas classes **Dog** and **Parrot** re-implement their own versions of **talk**.

## The Special Variable "super"

Occasionally, you may want to override a method, instead of using a method higher in the superclass chain. Generally, you'd do this whenever the specialized processing done by a method doesn't apply in a particular case. You would instead use the more general processing of a method with the same name which appears higher in the superclass chain.

For example, look at the method **talk** for class **Dog**. A dog doesn't know how to talk if its instance variable **barksAlot** is undefined (has the value **nil**). In this case, it uses the following message to request the superclass' **talk** method:

### super talk

The special variable **super** represents the same object as the special variable **self**: the receiver in the method in which it appears. The difference is that when a message is sent to **super**, Smalltalk looks for the method not in the receiver object's class, but instead in the superclass of the class containing the method in which **super** appears. In the example **talk** method, the search begins in **Mammal**, the superclass of class **Dog**. There is no **talk** method in class **Mammal**, but there is one in class **Animal**, so that one is used.

## Creating Animal Objects

Evaluate the following expressions to create and assign to global variables five animal objects: two dogs, a penguin, a parrot and a whale (the animals "talk" to the System Transcript, so first reframe the Disk Browser window to not overlap the System Transcript):
"creating animals"
Snoopy := Dog new.
Snoopy name: 'Snoopy'.
Snoopy beQuiet.
Lassie := Dog new.
Lassie name: 'Lassie'.
Lassie beNoisy.
Wally := Penguin new.
Wally name: 'Wally'.
Polly := Parrot new.
Polly name: 'Polly'.
Polly vocabulary: 'Polly want a Cracker'.
Moby := Whale new.
Moby name: 'Moby'

## Polymorphism

Polymorphism is a unique characteristic of object-oriented programming whereby different objects respond to the same message with their own unique behavior. For example, evaluate the following messages to see how the various animals respond to the talk message:

"let's hear them talk"
Lassie talk.
Snoopy talk.
Wally talk.
Polly talk; talk; talk.
Polly vocabulary: 'Screeech@#!? Don't bother me!'.
Polly talk.
Moby talk.
Snoopy beNoisy; talk.
Lassie beQuiet; talk

Polymorphism lets you use entirely new classes of objects in existing applications, as long as they implement the message protocol required by the application. This greatly facilitates the reusing of generic code. A simple example is the method max: defined in class Magnitude, which returns the "maximum" of two objects:

max: aMagnitude
self > aMagnitude
ifTrue: [^self]
ifFalse: [^aMagnitude]
The existing `max:` will work in any new subclass of `Magnitude`, as long as the new class implements the greater than (`>`) method.

## More General Pattern Matching

In Chapter 5, you created a method `indexOfString`: to do pattern matching on strings. By relocating this method to a superclass of class `String`, we can use it to do pattern matching for several more classes. We'll change the name of the method to `indexOfCollection`: to suggest its more general capability, but we won't change the processing. Use the Class Hierarchy Browser to add the method `indexOfCollection`: to class `IndexedCollection`, a subclass of `Collection`:

### `indexOfCollection: aCollection`
"Answer the index position of the first occurrence of a collection in the receiver. If no such element is found, answer zero."
```
| index1 index2 limit1 limit2 |
limit2 := aCollection size.
limit1 := self size - limit2 + 1.
index1 := 1.
[index1 <= limit1]
while True: [
    (self at: index1) = (aCollection at: 1)
    if True: [
        index2 := 2.
        [index2 <= limit2
        and: [(self at: index1 + index2 - 1) =
                (aCollection at: index2)]]
        while True: [index2 := index2 + 1].
        index2 > limit2
        if True: [^index1]].
    index1 := index1 + 1].
^0
```

Try the more general pattern matcher by evaluating the following examples using strings and arrays.

```python
'the time has come' of indexOfCollection: 'tim'
#($c $a $n $y $o $u $) of indexOfCollection: 'you'
#(1 2 3 (4 5) 'abc' 6) of indexOfCollection: #(2 3)
#(1 2 3 (4 5) 'abc' 6) of indexOfCollection: 'abc'
```
# Processing Recursive Data Structures

As an example of polymorphism and the processing of recursive data structures, consider the following method for equality (`==`), which appears in class `IndexedCollection`. This method compares an instance of class `IndexedCollection` or one of its subclasses (e.g., an array) to another similar object by sending the `==` message to corresponding elements of both objects. If the element is a kind of indexed collection, then the method performs a recursive send, invoking the `==` method. If the element is an object such as a number, the method performs a non-recursive send, invoking a different `==` method.

```java
    = aCollection
        "Answer true if the elements contained by
        the receiver are equal to the elements
        contained by the argument aCollection."`
    | index |
self == aCollection
    ifTrue: [^true].
(self class == aCollection class)
    ifFalse: [^false].
index := self size.
index =~ aCollection size
    ifTrue: [^false].
[index <= 0]
whileFalse: [
    (self at: index) = (aCollection at: index)
    ifFalse: [^false].
index := index - 1.]
^true
```

This method also demonstrates the difference between equality (`==`) and equivalence (`===`). Equality tests whether two objects contain the same elements. Equivalence, on the other hand, tests whether two objects are, in fact, the same object. For example, the expression

`self == aCollection`

tests whether the receiver object is the same actual object as the argument. If they are, then they are obviously equal, and the method returns `true`.

To help see this difference, evaluate the following statement:

```java
| a b |
a := #( 1 2 3 4 ).
b := #( 1 2 3 4 ).
^a = b
```
This expression returns true, because the two objects contain the same elements. Now substitute `= with ==`, and re-evaluate the statement. This returns false, because, although the two objects contain the same elements, they are still two different objects. As an example of a true equivalence, evaluate the following expression:

| a b c |
a := # ( 1 2 3 4 ).
b := a.
c := b.
^c == a.

To use the inherited = method on recursive data structures, evaluate these expressions:

\#(1 (2 (3))) = \#(1 (2 (3)))
\#(john smith) = \#(john smith)
\#(1 'two' 3) = \#(1 'two' 3)

Since the `indexOfCollection:` method defined above compares elements with the `=` message, it can be applied to nested (recursive) collections. For example, show the results of the following expressions:

\#((1 2)(3 4)(5 6)) `indexOfCollection:` \#((3 4)(5 6))
\#(1 2 3 (4 5) 'abc' 6) `indexOfCollection:` \#(3 (4 5) 'abc')
\#(1 2 3 (4 5) 'abc' 6) `indexOfCollection:` #('abc')

## A New Class: MonitoredArray

The final example of this chapter creates a new class to monitor the frequency of access to the data in an array. For instance, suppose you have an array of sales tax rates for California, indexed by zip code minus 90000 (California zip codes begin with 9). If you know how frequently each sales tax rate is looked up by zip code, you can compute the average sales tax paid, shipments to each region, and several other statistics.

To do this, we create class `MonitoredArray` as a subclass of class `Array`. A monitored array is like a normal array, except that it also maintains a parallel array containing the number of times the `at:` message was used for each index value. A monitored array can be substituted for an array in any application. Like any subclass, it inherits all the behaviors of its superclass (arrays), and implements some special behaviors of its own.

To add the new class, select class `Array` on the Class Hierarchy Browser. You'll find it as a subclass of `FixedSizeCollection`, which is a subclass of `IndexedCollection`, which is a subclass of `Collection`. With the cursor in the class list pane, pop up the pane menu and select `add subclass`. You'll then see a prompter, asking you for the `Array Subclass`
name; enter **MonitoredArray**. Finally, you'll see another menu, from which you should choose the **variableSubclass** entry. The class list is then updated, with class **MonitoredArray** selected.

Now you must specify the new class' instance variables. Proceed to the text pane below and edit the class definition to appear as follows:

```
Array variableSubclass: #MonitoredArray
instanceVariableNames: 'atCounts'
classVariableNames:"
poolDictionaries:"
```

Pop up the text pane menu and select the **save** entry. The class definition is updated.

## Class Methods

*Class methods* respond to messages sent to class objects, rather than to instances of the class. Class methods are often used for creating initialized instances of a class.

As an example, we'll create a class method for class **MonitoredArray**. Select class **MonitoredArray** using the Class Hierarchy Browser. Then select the pane labeled **Class**. This reverses the pane contents, indicating that any new methods added are class methods. Now pop up the method list pane menu, and select **new method**. The contents pane then displays a template reminding you of a new method's required components. Type the following method into the contents pane, replacing the template:

```
new: anInteger
    "Answer a new MonitoredArray."
| answer |
answer := super new: anInteger.
answer initialize.
^answer
```

Now pop up the pane menu, and select **save**, which adds this class method to class **MonitoredArray**. We re-implement **new**: for class **MonitoredArray**, because the inherited **new**: method for arrays does not initialize the `atCounts` instance variable.

The remaining three **MonitoredArray** methods are instance methods. Select the Class Hierarchy Pane labeled **Instance**, and add these three instance methods one at a time:

```
accessCounts
    "Answer the array of 'at:' counts."
^atCounts
```
at: anInteger

"Answer the element in the receiver at index position anInteger. Increment the count for accesses to the receiver using anInteger."

atCounts

at: anInteger

put: (atCounts at: anInteger) + 1.

^super at: anInteger

initialize

"Private - Initialize the MonitoredArray by allocating and initializing the parallel atCounts array."

| size |

size := self size.

atCounts := Array new: size.

1 to: size do: [ :index | atCounts at: index put: 0]

As an example of using a MonitoredArray, evaluate and show the results of the following expressions:

| array |

array := MonitoredArray new: 20.

1 to: 10 do: [ :i |

1 to: 10 do: [ :j | array at: i + j]].

^array accessCounts

## What You've Now Learned

After completing this tutorial, you should now be familiar with:

* the class hierarchy

* inheritance, both of methods and of variables

* polymorphism

* general pattern matching

* processing recursive data structures

* class methods

As always, you can review any of these topics by repeating the corresponding section of the tutorial, or by referring to the detailed description in Part 3, The Smalltalk/V 286 Reference.

If you exit the environment before beginning the next tutorial, be sure to save the image.
# 7 STREAMS AND COLLECTIONS

This chapter introduces you to two of Smalltalk's most widely used hierarchies: the stream classes and the collection classes. At the end of this chapter, you'll see four interesting examples using both of these hierarchies.

As always, the examples for this tutorial are stored in a disk file, *chapter.7*. You can use the Disk Browser to retrieve these examples, if you do not want to type them.

You will also be altering the image during this tutorial, so be sure to save the image when you exit the environment.

## Streams

Smalltalk supports many different kinds of stream objects. You already saw one of them when you accessed disk files using **FileStream** objects. Streams are also used for accessing the keyboard and mouse (class **TerminalStream**), and for accessing internal collections of objects, such as strings and arrays (classes **ReadStream**, **WriteStream**, and **ReadWriteStream**). The stream classes are arranged in a hierarchy with the class **Stream** as the superclass. You can use the Class Hierarchy Browser (explained in Chapter 6) to explore this hierarchy.

This chapter will present a series of examples using streams, which should give you a good introduction. **Part 3, The Smalltalk/V 286 Reference**, gives a detailed description of streams and all of the messages that can be used with them.

Streams are frequently used for scanning input or producing edited output. For example, look at this method, which does both:

```Smalltalk
"Replace occurrences of % with the date today"
| input output char dateStamp |
dateStamp := Date today printString.
input := ReadStream on: 'The date today is %'.
output := WriteStream on: String new.
NESS
    [input atEnd]
    whileFalse: [
        (char := input next) = $%
        ifTrue: [output nextPutAll: dateStamp]
        ifFalse: [output nextPut: char]].
NESS
    output contents
```

This example creates two streams. The `on:` message is sent to the class `ReadStream` to create a stream on the argument string 'The date today is %'. The `on:` message is also used to create a `WriteStream` on an empty string, to hold the edited output.
As the names imply, `ReadStream` can only be read and `WriteStream` can only be written. As we have seen previously with the disk file examples, streams are read with the *next* message and written with the `nextPut`: message. The message `atEnd` tests if there is more input to be read. The message `nextPutAll`: writes several objects to a stream at once. In the above example, the argument is a string of characters containing today's date.

The above example streams over strings of characters. It uses an empty string for the write stream because streams automatically grow as necessary, to accommodate the objects written to them. The `contents` message returns a string containing all of the objects written to the stream.

To change the above example to use disk files instead of streams on strings, simply change the messages that create the streams `input` and `output`. This illustrates one of Smalltalk's most powerful features: you can write programs that are dependent on the *behavior*, rather than the *structure*, of data. This means that you can write and test a program using simple internal objects, such as streams on strings, and then easily extend it to use external files.

Streams are not restricted to reading and writing only characters. For example, this method reads and writes arrays of numbers:

```Smalltalk
"Compute several factorials"
| input output |
input := ReadStream on: #(
    1 5 10 20).
output := WriteStream on: Array new.
input atEnd
whileFalse: [
    output nextPut: input next factorial].
^output contents
```

Although these examples do not show it, streams can also be repositioned, much like a random access file, using the `position`: message. The argument is an integer. You can also use the `position` message to access a stream's current position.

## Printer Stream

To see how easy it is to make major enhancements to `Smalltalk/V`, let's add a new class `PrinterStream`. This new class will allow you to use all of the stream messages with your printer. Define `PrinterStream` as a subclass of `WriteStream` (which is a subclass of `Stream`) with the type `subclass`. It needs no instance variables, class variables, or pool dictionaries. (Chapter 6 explains how to make new classes using the Class Hierarchy Browser.) After the class is created, the class definition displayed in the Class Hierarchy Browser should be:
WriteStream subclass: #PrinterStream
instanceVariableNames: "
classVariableNames: "
poolDictionaries: "

Class PrinterStream needs only two methods. Again, we've included these methods for you in a file. Evaluate the following expression to install them:

(File pathname: 'printrst7.st') fileIn

This installs the following methods:

nextPut: aCharacter
"Write aCharacter to the receiver."

| string |
string := ' '. "a string with one blank"
string at: 1 put: aCharacter.
string outputToPrinter.
^aCharacter

nextPutAll: aString
"Write aString to the receiver."

aString outputToPrinter.
^aString

Remember to use the update function from the class list pane menu to display the new methods.

PrinterStream will inherit its other methods (and all of its variables) from the classes WriteStream and Stream. To create an instance of the PrinterStream class in the global variable Printer, evaluate the following expression:

Printer := PrinterStream new

As an example of using a printer stream, produce a printed report by evaluating the following program:

"Print the first 10 even numbers and their factorials"

| width factorial |
width := 20 factorial printString size.

2 to: 20 by: 2 do: [:i |
factorial := i factorial printString.

Printer

next: 4 - i printString size put: Space;
nextPutAll: i printString, '';
next: width - factorial size put: Space;
nextPutAll: factorial;
cr]
If you do not have a printer, you can use the global variable Transcript instead of Printer in the above example. This will print the report in the System Transcript window. (The Transcript object is not a kind of stream, but it does support many of the same messages as streams.)

## Collections

Collections are objects which contain a collection of other objects. You have already seen two kinds of collections: Arrays and Strings. Strings are fixed sized sequences of characters, while arrays are fixed sized sequences of arbitrary objects. You have used the iterator messages *do*., *collect*., *select*., and *reject*.: with arrays and strings. These messages are understood by all of the collection classes, three of which are *Dictionary*, *Bag*, and *Set*.

## Dictionaries

Dictionaries store and retrieve objects by using a key. For example, let's create a simple phone book. First, create a global variable containing an empty dictionary by evaluating the following:

PhoneBook := Dictionary new

To add phone numbers to the phone book, use the `at:put:` message:

PhoneBook

at: 'Marisa' put: '645-1082';

at: 'Francesca' put: '555-1212';

at: 'Jackie' put: '392-481-5000';

at: 'Rakesh' put: '645-1083';

at: 'Vijay' put: '645-1083'

In the above expressions, the strings `'Marisa'` and `'Francesca'` are the keys, and the strings `'645-1082'` and `'555-1212'` are the corresponding values. Notice that `at:put:` is also used to access the elements of strings and arrays. With dictionaries, however, the first argument is the key in the dictionary, instead of the position in the array or string.

To retrieve an object from a dictionary, use the `at:` message with the key as the argument. For example the following expression returns the string `'645-1082':

PhoneBook at: 'Marisa'

To test if an object exists as a key in the dictionary, use the `includesKey:` message, as in the following expression:
(PhoneBook includesKey: 'Aaron')
ifTrue: [PhoneBook at: 'Aaron']
ifFalse: ['Not in phone book']

A simpler way to do this is to use the `at:ifAbsent:` message. The first argument is the key and the second argument is a block of code that will be executed if the key is not in the receiver dictionary. For example.

`PhoneBook at: 'Aaron' ifAbsent: ['Not in phone book']`

The keys and the values stored in a dictionary can be any kind of object.

Dictionaries are such useful objects that a special inspector window exists called the *Dictionary Inspector*. To open a dictionary inspector on the phone book, evaluate the following expression:

`PhoneBook inspect`

The pane on the left of the window is a sorted list of all of the keys in the dictionary, in our case the names of people in the phone book. When you select a key, the corresponding value is displayed in the pane on the right, in our case the person's phone number. By using the pane menus, entries can be edited, added, and removed.

## Bags

Bags store an arbitrary number of objects of any kind. Unlike arrays, there is no implied order or sequence to the elements (objects) inside the bag. Elements are added to a bag with the `add:` message. To test if an object is in a bag, use the `includes:` message. For example, this expression reads a file and reports the frequency with which each letter occurs:

| input answer f c |
input := File pathName: 'chapter.7'.
answer := WriteStream on: String new.
f := Bag new.
(input atEnd)
whileFalse: [
    (c := input next) isLetter
    ifTrue: [f add: c asLowerCase]].
0 to: 25 do: [ :i |
c := ($a asciiValue + i) asCharacter.
answer
cr; nextPut: c; space;
nextPutAll: (f occurrencesOf: c) printString].
^answer contents
## Sets

A Set, like a Bag, stores arbitrary objects. The difference is that a Set does not store the same object more than once. For example, this expression computes the set of characters that occur in one file and not in another:

```
| set1 set2 |
set1 := Set new.
set2 := Set new.
(File pathname: 'chapter.7') do: [:c| set1 add: c].
(File pathname: 'chapter.6') do: [:c| set2 add: c].
^set1 reject: [ :c | set2 includes: c]
```

The message asSet creates a set out of the receiver collection object. This is a good way to eliminate duplicates from a collection. For example, to compute the unique vowels in a string, evaluate the following:

```
'Now is the time' asSet select: [ :c | c isVowel ]
```

## Generic Code

You've now seen the iterator messages **select:**, **reject:**, and **collect**: used with strings, arrays, sets, and bags. These messages can be used with all of the different kinds of collections in Smalltalk/V. As such, they are excellent examples of generic code, code that is type and data independent. Smalltalk's ability to allow you to write generic code sets it apart from most other languages. Here is the code in class **Collection** for the **select:** message that is inherited by bags, sets, dictionaries, sorted collections, ordered collections, and other collection classes:

```
select: aBlock
| answer |
answer := self species new.
self do: [ :element |
          (aBlock value: element)
          ifTrue: [answer add: element]].
^answer
```

This method assumes nothing about the structure or type of the collections with which it deals. It depends only on an object's behavior, the existence of the **species**, **do:**, and **add**: it sends to them. Smalltalk's polymorphism (discussed in Chapter 6) makes this possible.

By exploring the **Collection** classes using the Class Hierarchy Browser, you can see many more examples of the power of generic code.
## Blocks as Objects

The `select:` message above shows another interesting feature of Smalltalk: the use of blocks of code as objects. To illustrate this, look at the following invocation of the `select:` method.

`'Now is the time' asSet select: [ :c | c isVowel ]`

The receiver of the `select:` message is the set of all characters in the string `'Now is the time'`. The argument to the `select:` message is a block of code with one block argument, `[ :c | c isVowel ]`. This block of code is as much an object as the string `'Now is the time'`. As such, we can use it as an argument for the `select:` method, which you saw previously. When the method is invoked, the block of code is assigned to the argument `aBlock` in the `select:` method.

A block of code executes when it is sent the message `value`, `value:`, or `value:value:`, depending on whether the block has zero, one, or two block arguments, respectively. Since it uses one argument, the `select:` message evaluates the block `aBlock` using the `value:` message.

As you now know, all messages return a result. The result of evaluating a block is the result of the last expression in the block. In the above example, the block `[ :c | c isVowel ]` returns `true` or `false`, depending on whether or not the object passed to the block, `c`, is a vowel.

## Patterns

Block objects in turn let you build very powerful objects. For example, look at the class `Pattern`. Patterns are generalized and efficient pattern matchers. A pattern object consists of a collection of objects to match, and a block of code to execute when the pattern is successfully matched. For example, this expression computes the number of occurrences of a phrase in a file:

```
"Compute occurrences of a phrase in a file"
| pattern count input word |
count := 0.
 pattern := Pattern new: #('now' 'is' 'the')
   matchBlock: [count := count + 1].
input := File pathName: 'chapter.7'.
 [ (word := input nextWord) isNil]
   whileFalse: [pattern match: word aslowerCase].
^count
```

This example uses an array of strings as the pattern. Any collection of objects can be used as the pattern, as long as it can be indexed using the `at:` message.
## Computing Letter Pair Frequencies

The following example computes the frequency with which letter pairs occur in a file, and stores the result in the global variable, `Pairs`:

```h
"compute letter pair frequencies"
| last pair |
Pairs := Bag new.
last := Space.
(File pathname: 'chapter.7') do: [ :c |
(last isLetter and: [c isLetter])
  ifTrue: [
    (pair := String new: 2)
    at: 1 put: last;
    at: 2 put: c.
    Pairs add: pair aslowerCase].
last := c]
```

The following expression, in turn, produces a report of the pair frequencies that occur more than 60 times in `Pairs`:

```h
"print letter pair frequencies greater than 60
in the Transcript"
| frequent |
Transcript cr.
frequent := Pairs asSet select: [ :pair |
(Pairs occurrencesOf: pair) > 60 ].
frequent asSortedCollection do: [:pair |
Transcript
  nextPutAll: pair;
  tab;
  nextPutAll: (Pairs occurrencesOf: pair)
    printString;
  cr]
```

The message `asSortedCollection` creates a new kind of collection, a `SortedCollection`. `SortedCollections` are described in detail in Part 3, The Smalltalk/V 286 Reference. Briefly, they are collections in which all of the elements are stored in sorted order. As you can see from the above example, they are useful for sorting a collection of objects before outputting a report.
<figure><img src="image_35.png" /></figure>

## Animals Revisited

In Chapter 6, we built a simple hierarchy of animal classes. In this section, we will give those animals an environment (habitat) in which to live and a way to acquire knowledge and interact with their habitat.

The habitat will have a set of animals that inhabit it. Every animal will store knowledge as a collection of patterns, instances of class **Pattern**. In this case a pattern is a sequence of words that, when recognized by the pattern, evaluates a corresponding block of code. This causes the animal to react to a word sequence in some prescribed way. The global variable **Script** contains a stream of words to send to all of the animals. Giving many different patterns to a single animal provides that animal with a rich set of behaviors.

## Animal Habitat

Create a new class **AnimalHabitat** as a subclass of class **Object**, and assign to it five instance variables, **animals**, **replyStream**, **animator**, **inputString**, and **inputPane**. (Chapter 6 explains how to do this using the Class Hierarchy Browser.) When the new class is successfully created with the five instance variables, you should see the following class definition when it is selected in the Class Hierarchy Browser:

```
Object subclass: #AnimalHabitat
instanceVariableNames:
    'animals replyStream animator
    inputString inputPane'
classVariableNames: "
poolDictionaries: "
```

The instance variable **animals** will contain the set of animals that inhabit the habitat. (The instance variables **replyStream**, **animator**, **inputString**, and **inputPane** are used in a later tutorial.)

Now evaluate the following expression to file in the methods for the **AnimalHabitat**:

(File pathname: 'habitat7.st') fileIn

Click the cursor over the instance label to view the methods list. The new methods are:

```
add: anAnimal
    "Add anAnimal as an inhabitant of the receiver.
    Notify anAnimal of its new habitat."
animals isNil
    ifTrue: [animals := Set new].
animals add: anAnimal.
anAnimal habitat: self
```
play

"Play the Script to all of the animals."

| word |

Script reset.

animals do: [ :animal | animal reset ].

[Script atEnd]

whileFalse: [

word := Script next asLowerCase.

animals do: [ :animal | animal reactTo: word ] ]

script: aString

"Change Script to the stream on the
words in aString."

| stream word |

stream := ReadStream on: aString.

Script := ReadWriteStream on: Array new.

[(word := stream nextWord) isNil]

whileFalse: [

Script nextPut: word]

Now evaluate the following expression to create a global variable, Habitat, containing an
instance of the AnimalHabitat class:

Habitat := AnimalHabitat new

Animal Knowledge

To put animals inside of the habitat, you must first add some methods to the Animal
class. Evaluate the following expression to add the required methods:

(File pathname: 'animal7.st') fileIn

The new methods in class Animal are:

habitat: aHabitat

"Change habitat to aHabitat"

habitat := aHabitat
<figure><img src="image_36.png" /></figure>

**learn:** aString action: aBlock

"Add a pattern of the words in aString to the receivers knowledge. The action to perform when the pattern is matched is aBlock."

| words pattern |

knowledge isNil

ifTrue: [knowledge := Dictionary new].

words := aString aslowerCase asINKOfSubstrings.

pattern := Pattern new:

(Array with: name aslowerCase), words.

pattern matchBlock: aBlock.

knowledge at: words put: pattern

**reactTo:** aWord

"Send a word to every pattern in knowledge."

knowledge isNil

ifTrue: [^self].

knowledge do: [:pattern | pattern match: aWord]

**reset**

"Reset all patterns in knowledge"

knowledge isNil

ifTrue: [^self].

knowledge do: [:pattern | pattern reset]

## Using the Habitat

First, let's add some animals to the habitat. The following expressions use the animals that were created in Chapter 6:

**Habitat**

add: Snoopy;

add: Polly

Now, set up a script to work with:

**Habitat script:**

'Snoopy is upset about the way that Polly is behaving. It is as if whenever anyone asks Polly to talk, Polly will be nasty. Maybe if instead of Snoopy barking at Polly when he wants Polly to talk, Snoopy quietly asks Polly to be pleasant for a change, things would go better. Now maybe Snoopy barking quietly will not make Polly nasty.'
Before playing the script, we need to give the animals some knowledge:

### Snoopy

learn: 'barking' action: [Snoopy talk];
learn: 'quietly' action: [Snoopy beQuiet; talk];
learn: 'is upset' action: [Snoopy beNoisy; talk].

### Polly

learn: 'to be pleasant' action:
[Polly vocabulary: 'Have a nice day'; talk];
learn: '*' nasty' action:
[Polly vocabulary: 'Why are you bothering me';
talk].

The asterisk (*) in '*' nasty' stands for none or more arbitrary words. To play the script to the animals, evaluate the following expression:

### Habitat play

Look in the System Transcript to see the responses from the animals.

## A Network of Nodes

As a final example of streams and collections, we will build a network of nodes, and determine paths through the network. Many problems can be described in terms of networks of nodes and paths through the network, such as route maps, pert charts, and many kinds of optimization problems.

### Network

A *network* is a collection of nodes that are connected to each other. Create the class *Network* as a subclass of class *Object*, and define a single variable named *connections*. When you have created the class and the instance variable, the class specification in the *Class Hierarchy Browser* should be:

Object subclass: #Network
instanceVariableNames:
'connections'
classVariableNames: ''
poolDictionaries: ''

The instance variable *connections* will hold a dictionary of connections between nodes. The key to the dictionary will be a node, and the value stored under that key will be a set of all of the nodes to which it is connected. Use the following expression to file in the methods for class *Network*:
(File pathname: 'network7.st') fileIn

The methods are:

connect: nodeA to: nodeB

"Add a connection from nodeA to nodeB."

(connections

at: nodeA

ifAbsent: [connections at: nodeA put: Set new])

add: nodeB.

(connections

at: nodeB

ifAbsent: [connections at: nodeB put: Set new])

add: nodeA

initialize

"Initialize the connections to be empty."

connections := Dictionary new

pathFrom: nodeA to: nodeB avoiding: nodeSet

"Answer a path of connections that connect nodeA to nodeB without going through the nodes in nodeSet. This result is returned as a new network. Answer nil if there is no path"

| answer |

nodeSet add: nodeA.

(connections at: nodeA ifAbsent: [^nil]) do:

[ :node |

node = nodeB

ifTrue: [

^Network new initialize

connect: nodeA to: node].

(nodeSet includes: node)

ifFalse: [

answer := self

pathFrom: node

to: nodeB

avoiding: nodeSet.

answer isNil

ifFalse: [

^answer connect: nodeA to: node]]].

^nil
printOn: aStream

"Print a description of the receiver on aStream."

connections keys asSortedCollection do: [ :node |

node printOn: aStream.

(connections at: node) asSortedCollection do:

[ :neighbor |

aStream

cr;

nextPutAll: '>>'.

neighbor printOn: aStream].

aStream cr]

Notice the recursion in the `pathFrom/to:avoiding:` message. This is a simple solution; it does not find the optimal (shortest) path. If you want to find such an optimal solution, however, you need only change this one method. This is another of Smalltalk/V's characteristics. You can quickly build program fragments to start exploring the nature of the problem being solved. When you better understand the problem, the changes are quick and localized.

## Network Nodes

Before using the **Network** class, define the class **NetworkNode** as a subclass of class **Object**, with two instance variables, **name** and **position**. After you have created the class and its instance variables, the class specification should be:

Object subclass: #NetworkNode

instanceVariableNames: 'name position'

classVariableNames: "

poolDictionaries: "

Then use the following expression to file in the methods for class **NetworkNode**:

(File pathname: 'nodes7.st') fileIn

The methods are:

<= aNode

"Answer true if the receiver name is less or
equal to aNode name."

^name <= aNode name

hash

"Answer receiver's hash."

^name hash
name

"Answer receiver's name."

^name

name: aString position: aPoint

"Set the receivers name and position."

name := aString.

position := aPoint

printOn: aStream

"Print a description of the receiver on aStream."

aStream

nextPutAll: 'Node(', name;

space;

nextPutAll: position printString;

nextPut: $)

## Building a Network

Now evaluate the following expression to create an empty network in the global variable Net:

Net := Network new initialize

Then evaluate these expressions, to create six nodes and connect them together into a network:

N1 := NetworkNode new name: 'one' position: 300 @ 100.

N2 := NetworkNode new name: 'two' position: 400 @ 150.

N3 := NetworkNode new name: 'three' position: 500 @ 120.

N4 := NetworkNode new name: 'four' position: 200 @ 50.

N5 := NetworkNode new name: 'five' position: 350 @ 195.

N6 := NetworkNode new name: 'six' position: 550 @ 130.

Net

connect: N1 to: N2;

connect: N2 to: N3;

connect: N4 to: N5;

connect: N5 to: N1;

connect: N3 to: N6;

connect: N3 to: N5;

connect: N3 to: N1

You can ask the network to print itself by evaluating the following expression using show it:

Net
Now evaluate the following expression and show the results, to find a path from N1 to N5:

Net pathFrom: N1 to: N5 avoiding: Set new

To see if there is a path that does not go through N3, evaluate the following expression:

Net pathFrom: N1 to: N5 avoiding: (Set with: N3)

## What You've Now Learned

After having completed this tutorial, you should be familiar with:

* streams, including PrinterStream

* collections, including Dictionaries, Bags, and Sets

* generic code

As always, you can review any of these topics by repeating the corresponding section of the tutorial, or by referring to a detailed description in Part 3, The Smalltalk/V 286 Reference.

If you exit the environment before beginning the next tutorial, be sure to save the image.
# 8 DEBUGGING

This chapter uses what you learned in Chapter 7 to build a complete program using collections and streams. The program, however, purposely contains several errors. This chapter, then, shows you how to locate and correct errors using the Smalltalk/V debugger.

As always, the examples for this chapter are stored in the disk file, *chapter.8*. You can use the Disk Browser to retrieve these examples.

Since you will again be making modifications to your Smalltalk/V environment, be sure to save the image when you exit Smalltalk/V.

## A Document Retrieval System

The first thing we'll do is implement a new class, `WordIndex`, which allows you to create a database of documents and locate them based upon the words that they contain. `Documents` are ASCII text files, viewed as a series of words containing alphanumeric characters separated by a series of non-alphanumeric characters. You query the database by supplying a collection of word strings, which returns a collection of the file names of all the documents that contain all the words. You could use the word index, for example, to locate resumes in a personnel system, such as all employees whose resumes contain the words C and Unix.

Instances of class `WordIndex` have instance variables `documents` and `words`.

`documents` is a set of strings of the document file path names whose words have been entered into the word index.

`words` is a dictionary, with each key containing a string for a word and each value being a set containing the path names of all documents containing the word.

Therefore, the class definition is:

```
Object subclass: #WordIndex
instanceVariableNames:
    'documents words'
classVariableNames:"
poolDictionaries:"
```

Add `WordIndex` class definition and methods to your Smalltalk/V image by evaluating the following expression:
(File pathname: 'wrdidx8.st') fileIn

Now select update in the Class Hierarchy Browser's class list so that you can browse the methods of class WordIndex. There are six methods defined for class WordIndex, as follows:

addDocument: pathname

"Add all words in document described by pathname string to the words dictionary."

| word wordStream |

(documents includes: pathname)

ifTrue: [self removeDocument: pathname].

wordStream := File pathname: pathname.

documents add: pathname.

[(word := wordStream nextWord) == nil]

whileFalse: [

self addWord: word aslowerCase to: pathname].

wordStream close

addWord: wordString for: pathname

"Add wordString to words dictionary for document described by pathname."

(words at: wordString) add: pathname

initialize

"Initialize a new empty WordIndex."

documents := Set new.

words := Dictionary new

locateDocuments: queryWords

"Answer an array of the pathNames for all documents which contain all words in queryWords."

| answer bag |

bag := Bag new.

answer := Set new.

queryWords do: [ :word |

bag addAll:

(documents at: word ifAbsent: [#()]).

bag asSet do: [ :document |

queryWords size =

(bag occurrencesOf: document)

ifTrue: [answer add: document]].

^answer asSortedCollection asArray
```clover
removeDocument: pathname
  "Remove pathname string describing a
document from the words dictionary."
words do: [ :docs | docs remove: pathname ].
self removeUnusedWords
```

```clover
removeUnusedWords
  "Remove all words which have empty
document collection."
| newWords |
newWords := Dictionary new.
words associationsDo: [ :anAssoc |
  anAssoc value empty
  ifFalse: [newWords add: anAssoc]].
words := newWords
```

## How Class WordIndex Works

Next, we'll describe class WordIndex in terms of the high-level messages which create an index and make queries.

We mentioned earlier that we've included some intentional errors; this is the first place where they occur. For this reason, don't evaluate these messages until the tutorial tells you to do so.

We'll construct and use the word index in three steps. First, we create an empty word index in an expression such as the following (remember, don't evaluate this expression yet):

```clover
Index := WordIndex new initialize
```

The initialize method initializes instance variables of the WordIndex; that is, documents now contains an empty set and words contains an empty dictionary.

Next, we'll add the words from documents to the WordIndex. The addDocument: method creates a file stream to scan the document, repeatedly sends the nextWord message to the file stream to obtain each word, and then uses the addWord:for: method to enter each word/document pair in the words dictionary. For example, to add the words from the Chapters 5 and 6 sample files, you would use the following expressions (again, don't evaluate these yet):

```clover
Index addDocument: 'chapter.5'.
```

```clover
Index addDocument: 'chapter.6'.
```

To query the word index, you use the locateDocuments: message, as in the following examples (again, do not evaluate them):
Index locateDocuments: #('show' 'class')

Index locateDocuments: #('where' 'the' 'turtle')

Index locateDocuments: #('each' 'talk')

Each query above returns an array of strings, containing the document path names for all documents that contain all words in the query. The locateDocuments: method is somewhat more complex than the other methods in its class. It uses a bag to accumulate all the path names of all the files that contain each word in the query. (Remember that bags, unlike sets, can contain multiple occurrences of the same object.) It then examines the bag to find any documents which are repeated as many times as there are words in the query; these are the documents which contain all the words.

Debugging Class WordIndex

Now that you've seen how this class is supposed to work, let's see if it does. (From this point, start evaluating the sample expressions again.) First, build a new word index and assign it to the global variable Index:

Index := WordIndex new initialize

Now try adding the tutorial files for Chapters 5 and 6 by evaluating the following addDocuments: messages:

Index addDocument: 'chapter.5'.

Index addDocument: 'chapter.6'.

Oops! Instead of adding the tutorial files, we get a walkback window:

<figure><img src="image_37.png" /><figcaption>Figure 8.1 Walkback Window</figcaption></figure>
As you saw in Chapter 2, a walkback window describes an error condition. The label shows the error condition, and the text pane shows the most recently sent messages, with those most recently sent appearing first.

In the above walkback window, the label says that the `addWord.to:` message is not understood, while the top line in the text pane shows `WordIndex` as the class of the object which did not understand the message.

Whenever you get a walkback window, you generally do one of three things:

1. You can determine what the problem is from the information contained in the walkback window. In this case, you normally close the walkback window and then go fix the problem.

2. You can determine that the walkback window occurred either as a result of you typing the control and break keys simultaneously, or because a **halt** message was sent. In this case, there is nothing wrong with the program, so you can pop up the pane menu for the walkback window and select **resume**. The walkback window closes and execution continues.

3. You can decide that you need more information, and would like to use the *debugger* to obtain it. In this case, you pop up the pane menu for the walkback window and select **debug**. The walkback window closes and the debugger window opens.

In our case, we probably have enough information in the walkback window to fix the problem. Look at the code for class `WordIndex` using the Class Hierarchy Browser. We defined a method `addWord:for:`, but sent the message `addWord.to:` (in the `addDocument:` method) which was not understood. We used the wrong message!

Correct the `addDocument:` method to use `addWord:for:` instead of `addWord:to`, and then try again to add the tutorial files to the word index, using the following expressions.

Index `addDocument: 'chapter.5'`.

Index `addDocument: 'chapter.6'`.

Not fixed yet! This time, you get a new walkback window:
<figure><img src="image_38.png" /><figcaption>Figure 8.2
Opening
the Debugger</figcaption></figure>

The label of the walkback window says **Key is missing**. Since the problem is not obvious, let's see if we can get some more information by using the debugger. Pop up the walkback window pane menu and select **debug**. You'll see the following debugger window:

<figure><img src="image_39.png" /><figcaption>Figure 8.3
Debugger with
Window Buttons</figcaption></figure>

The debugger window gives you an expanded view of the walkback in several panes. The top left pane (a list pane) repeats the walkback information; you can use this pane to select walkback lines. When you select a walkback line, the other panes contain related information. Select the entry containing **Dictionary >> at:**.

The bottom pane displays the source code for the selected method, in this case **at**: from class **Dictionary**:
```
at: aKey
| answer |
^ (answer := self lookUpKey: aKey) == nil
    ifTrue: [self errorAbsentKey]
    ifFalse: [answer value]
```

The text that is reversed is the expression currently being evaluated in this method.

As you can see, this method invokes another dictionary method `lookUpKey:`, and then invokes `errorAbsentKey` if the key is missing, which eventually results in the walkback window.

The two panes on the top right are an inspector for the receiver, arguments and temporary variables of the selected method. In this case, you see the receiver `self`, the argument `aKey` and the temporary `answer`. Select `self`; you see that the value is an empty dictionary. Now select `aKey`; the value is the string `'tutorial'`, the first word in the file. We tried to do a dictionary lookup on an empty dictionary, `self`, with the first word in the file as key.

Select the line containing `addWord:for:` in the walkback pane on the top left of the window. Now select the argument `wordString`. Again, it's the string `'tutorial'`. We tried to access the words dictionary with a key, without first testing whether or not the key is present! Correct the `addWord:for:` method in the bottom pane of the debugger to look as follows:

```
addWord: wordString for: pathname
    "Add wordString to words dictionary for
document described by pathname."
(words includesKey: wordString)
    ifFalse: [words at: wordString put: Set new].
(words at: wordString) add: pathname
```

Now pop up the bottom pane menu and select `save`. Notice what happens. The entries above `addWord:for:` in the walkback list are discarded, because a method they would return to has been changed. The `addWord:for:` method is still selected. Now pop up the menu in the walkback list pane and select `restart`. Execution resumes by re-sending the selected message.

The debugger window disappears, and the method builds the index. With the dictionary now built, let's try to make some queries. Try evaluating the expression below.

```
Index locateDocuments: #('show' 'class')
```

Another walkback window pops up, indicating that there is another error. Immediately open a debugger window on this new error. You'll see the following window:
<figure><img src="image_40.png" /><figcaption>Figure 8.4
Inspecting
Variables</figcaption></figure>

The message `at:ifAbsent:` was sent to an instance of class `Set`, which did not understand it. Select the top walkback line containing `Set(Object) >> doesNotUnderstand:`, and then select `self` in the temporary variable list. The value is:

`Set('chapter.6' 'chapter.5')`

Now select the third walkback line, representing a block in the `locateDocuments:` method, and examine the values of the temporary variables. Then look at the source code for the method. The `at:ifAbsent:` message being executed is reversed. It uses instance variable `documents` as receiver. The value printed out for the set above confirms this, because it does contain the document path names.

Let's look at this statement. Either we sent the wrong message to `documents` or `documents` is the wrong receiver. This statement is trying to add to variable `bag` all the documents that include the string contained in variable `word`. The receiver is indeed wrong. This statement should instead use the `words` dictionary:

`bag addAll: (words at: word ifAbsent: [#()])`

Change the `locateDocuments:` method using the debugger, save it, and restart at `locateDocuments:`. It works! Try the following queries:

```
Index locateDocuments: #('where' 'the' 'turtle')
Index locateDocuments: #('each' 'talk')
```
## Hop, Skip and Jump

Now that you have class WordIndex debugged, let's see how you can use the debugger to learn how an application operates by watching it send messages. Open a debugger window to step through execution of the query you just performed by evaluating the following expression:

```python
self halt.
Index locateDocuments: #(each talk)
```

<figure><img src="image_41.png" /><figcaption>Figure 8.5 Debugging an Expression</figcaption></figure>

There are 6 buttons on the right side of a debugger window label bar as seen in Figure 8.5. The first three called hop, skip and jump are related to debugging. Hop, skip and jump each cause limited program execution. Hop executes the least amount: one Smalltalk message send or assignment statement. Skip executes more than hop: up to the next message send or assignment in the current method or up to the next breakpoint, whichever comes first. (Refer to Chapter 16 for a description of breakpoints). Jump executes more than skip: up to the next breakpoint or the end of the debugged expression.

Try selecting the hop button twice and watch the debugger window. Execution state is now at the beginning of execution of the expression, shown in Figure 8.5. Select hop again. Notice how execution proceeds in small amounts, with the next statement to be executed highlighted after the step. You can examine the state of objects after each hop.

Now try selecting the skip button a few times. Notice that the highlighting stays within the same method until the method finishes execution. This allows you to concentrate on a single method activation and ignore lower level messages.
What You've Now Learned

By the end of this tutorial, you should be familiar with walkback windows and how to use the debugger. If you want to review, you can either repeat the tutorial, or refer to the detailed description in Part 3, The Smalltalk/V 286 Reference.

As always, if you exit Smalltalk/V before beginning the next tutorial, be sure to save the image.
# 9 GRAPHICS

In these tutorials, you have seen some of Smalltalk/V's remarkable graphics. In this tutorial, you will learn how Smalltalk/V produces such graphics.

The examples in this chapter repeatedly alter the top half of the screen. For this reason, you will want to open any windows in the bottom half of the screen only. After each example, you can restore the screen to its previous state by selecting *redraw screen* from the system menu. As always, you can find the sample code for this tutorial in the disk file *chapter.9*. Use the Disk Browser to retrieve these examples from this file. Since this file is larger than 10,000 bytes, only the beginning and end of it can be seen in the text pane. Pop up the text pane menu and select *read it* to make the entire file accessible.

You will again be adding new methods and classes to the environment in this tutorial, so be sure to save the image when you exit Smalltalk/V.

## Some Basic Concepts

Smalltalk/V owes its graphical capability to *bit-mapped graphics* (also called *raster graphics*). A line is drawn with a continuous vector of dots. A cursor is formed with a rectangle of black and white dots. Even a character is formed with a block of dots, instead of an ASCII value.

These dots are displayed on a monitor screen as colored pixels. They are stored internally as a *Bitmap*, contained in a *Form*. A *Bitmap* is a matrix of bits, with a value 1 representing white and 0 representing black. To refer to an individual dot within a *Bitmap*, you use *Points*. To move a group of dots from one place to another (either within the same *Bitmap* or between different *Bitmaps*), you use *Rectangles* to denote the areas involved. Thus *Point*, *Rectangle*, and *Form* are Smalltalk/V's basic graphic data structures.

### Point

A Point refers to a position within a two dimensional array. It has two instance variables: *x*, the column coordinate, and *y*, the row coordinate. To create a Point, you use the binary message `@`. For example, the expression:

`5 @ 10`

creates a Point referencing column 5 and row 10. Evaluate the following expressions:

`(5 @ 10) x`

`(5 @ 10) y`
These expressions return the values 5 and 10, respectively. You can also add, subtract, multiply, divide, or compare Points, as in these examples:

$$ (1 \text{ @ } 2) + (-1 \text{ @ } -2) $$

$$ (1 \text{ @ } 2) - (1 \text{ @ } 2) $$

$$ (1 \text{ @ } 2) * (1/2 \text{ @ } (1/2)) $$

$$ (1 \text{ @ } 2) < (3 \text{ @ } 4) $$

$$ (3 \text{ @ } 5) > (3 \text{ @ } 4) $$

These expressions combine or compare x of the receiver with x of the argument and y of the receiver with y of the argument. This is why the last expression returns false; the first x, 3, is not greater than the second, also 3.

You can mix a Point with a scalar:

$$ 1 \text{ @ } 2 + -1 $$

$$ 1 \text{ @ } 2 // 2 $$

which applies the scalar to both x and y of the Point. You cannot, however, compare point coordinates and a scalar. For example, try evaluating this expression:

$$ 1 \text{ @ } 2 < 3 $$

To alter one of the two coordinates, simply use the messages x: and y:. For example, evaluate the following expressions as a group:

| aPoint |

aPoint := (5 @10).

aPoint x: 1.

aPoint y: 2.

aPoint

## Rectangle

A Rectangle is represented by two points: an origin (the top left point) and a corner (the bottom right point). With this information, Smalltalk can determine its extent (the width and height of the elements contained within the rectangle) as:

### corner - origin

To create a Rectangle, you normally send messages to a point, as in this example:

1 @ 1 corner: 100 @ 100

Or equivalently:

1 @ 1 extent: 99 @ 99
A Rectangle includes the bits *inside* of the rectangle. The rectangle itself is imposed on gaps between bits. For example, the Rectangle

1 @ 1 corner: 4 @ 3

contains 6 bits (3 horizontal and 2 vertical) as illustrated below:

<figure><img src="image_42.png" /><figcaption>Figure 9.1
Pixels and
Rectangles</figcaption></figure>

There are many operations you can perform on Rectangles. For example, try evaluating each of the following:

(0 @ 0 extent: 100 @ 100) center

(0 @ 0 extent: 100 @ 100) insetBy: 10

(-5 @ -10 extent: 20 @ 20)

intersect: (1 @ 2 extent: 20 @ 20)

(-5 @ -10 extent: 20 @ 20)

containsPoint: 0 @ 0

These operations are not the focus of this tutorial, however; refer to Part 3, The Smalltalk/V 286 Reference, for more detailed descriptions.

## Form

As we said before, a Point or a Rectangle simply refers to a position or an area of positions. The object that actually holds the graphical image is the *Form*. A Form has many instance variables, but only three concern you: **bits**, **width**, and **height**.
The variable bits contains a Bitmap, which is the content of the Form. This Bitmap contains bits for the area represented by a Rectangle:

`0 @ 0 extent: (width @ height)`

So, for example, to create a Form containing all 1 bits, evaluate the following expression:

`F := Form width: 100 height: 50`

To display a Form, use the message `displayAt:` with a point for the argument. For example, to display the rectangle created above, you would use:

`F displayAt: 0 @ 0`

This displays a white rectangle at the top left corner of the screen. You can also display only a portion of a Form. For example, evaluate the following expression:

`| black white aRect |`

`white := Form width: Display width height: Display height.`

`black := (Form width: Display width height: Display height) reverse.`

`aRect := Display boundingBox. "screen rectangle"`

`Display height // 2 // 8 timesRepeat: [`

`black displayAt: 0 @ 0 clippingBox: aRect.`

`aRect := aRect insetBy: 4.`

`white displayAt: 0 @ 0 clippingBox: aRect.`

`aRect := aRect insetBy: 4].`

`Menu message: 'continue'.`

`Scheduler systemDispatcher redraw`

The message `displayAt:clippingBox:` restricts the area on the screen to be changed. (You'll learn more about clipping boxes later in this tutorial.) The last two lines pop up a menu and then redraw the screen.

You can also display a Form on a printer by evaluating the following expression:

`(Form new width: 100 height: 100 initialByte: 16rF0) outputToPrinter`

Class Form has three immediate subclasses: `BiColorForm`, `ColorForm`, and `DisplayScreen`.

A `BiColorForm` allows a foreground color to be assigned to the 1 pixels, and a background color to the 0 pixels in the bitmap. This allows a single bitmap to represent two colors other than just black and white.

A `ColorForm` has an array of bitmaps. The bits in the same position of each bitmap collectively represent the color of the pixel at that position.
Class `DisplayScreen` and its subclass `ColorScreen` represents a monochrome screen and a color screen respectively. Their bitmaps have fixed physical addresses, as well as fixed sizes, dictated by the mode of the graphics adapter being used. A global variable, `Display`, contains an instance of either `ColorScreen` or `DisplayScreen` depending on whether or not your monitor and graphics adaptor supports color.

## The Basic Class of Graphics: BitBlt

BitBlt ("bit block transfer") is the fundamental class of all Smalltalk/V graphics. Classes like `Pen` (for drawing) and `CharacterScanner` (for writing text) are subclasses of BitBlt.

The basic function of BitBlt is to move a rectangular area of bits from one portion of a Form or DisplayScreen to another. The simplest form of the move requires a source form, a destination form, a rectangle on the source form to be moved, and an origin point on the destination form. Smalltalk can then calculate the corner of the destination rectangle by adding the source rectangle extent to the destination origin.

For example, consider the following code, which copies the top left quarter of the screen to the right:

(BitBlt destForm: Display sourceForm: Display)
    sourceRect: (0 @ 0 extent: Display extent // 2);
    destOrigin: (Display width // 2 @ 0);
    copyBits

The first line creates a BitBlt instance with `Display` as both the source and destination form. The second line specifies the top left quarter of `Display` as the source rectangle. The third line specifies the top center point as the destination origin. The last line moves the bits.

Let's look at another example. This code copies a white form in its entirety to `Display`, while storing the old contents of the screen in another form, `F`:

F := Display compatableForm new extent: Display extent // 2.
(BitBlt destForm: F sourceForm: Display) copyBits.
(BitBlt destForm: Display sourceForm:
(Form new extent: F extent)) copyBits.

The first line creates a form, `F`, with a size one quarter of the display screen. `Display compatableForm` returns either class `Form` or class `ColorForm` depending on whether `Display` is an instance of class `DisplayScreen` or `ColorScreen`, respectively. The second line copies the top left quarter of the screen onto `F`. When, as in this example, you do not specify a source rectangle and destination origin, the `destForm:sourceForm:` message uses the entire area of the source form and point 0 @ 0 as the source rectangle and destination origin, respectively.
In this copy operation, **BitBlt** copies a larger rectangle onto a smaller form. This poses no problem, however; **BitBlt** never touches any bits beyond those contained in the destination form.

The last two lines create a new white form with the same size as F, and then copies it onto the screen. Here, we move a smaller form to a larger one. Again, **BitBlt** moves only as many bits as are contained in the source form. Now evaluate the above expressions; the upper left corner is blanked. But, since the old contents are stored in F, you can restore the old screen by evaluating:

(BitBlt destForm: Display sourceForm: F) copyBits

The rest of this section describes the concepts of **BitBlt** with color.

When bits are moved from a **BiColorForm** to either a **ColorForm** or **ColorScreen**, the bits of value 1 in the **BiColorForm** become the foreground color specified by the mask form and the bits of value 0 become the background color. Then these color pixels are moved to the destination with four bits of each color pixel occupying the same position on the four destination bitmaps. Try the following:

| aForm |
**aForm** := **BiColorForm** width: 100 height: 100.
(BitBlt destForm: aForm sourceForm: nil)
  mask: Form black;
  extent: 50 @ 100;
  copyBits;
  destForm: Display;
  sourceForm: aForm;
  mask: (BiColorForm foreColor: 1 backColor: 4);
  extent: 100 @ 100;
  copyBits

This example first initializes aForm to be a **BiColorForm** with all bits set to 1. Then it copies zeros into the bits comprising the left half of aForm. Finally it copies aForm to the display screen assigning color 1 (blue) to 1 bits and color 4 (red) to 0 bits. The result shown on the screen is a 100 x 100 pixel block with the left half in red and the right half in blue.

When bits are moved between a **ColorForm** and a **ColorScreen**, bits are moved between corresponding bitmaps. In other words, this can be viewed as four separate moves between corresponding single bitmaps. Try the following:
```pascal
| aForm |
aForm := ColorForm width: 100 height: 100.
       (BitBlt destForm: aForm sourceForm: Display)
       copyBits;
       destForm: Display;
       sourceForm: aForm;
       mask: Form gray;
       copyBits
```

This example copies a block from `DisplayScreen` to a `ColorForm` and then copies it back to the screen with a gray halftone.

When bits are moved from a `ColorForm` or `ColorScreen` to a `BiColorForm`, the colors on the source bitmaps that are the same as the foreground color of the mask form are turned into 1 bits on the destination `BiColorForm` and the rest of the bits are set to zero. For example,

```pascal
| aForm |
aForm := BiColorForm
       width: Display width
       height: Display height.
       (BitBlt destForm: aForm sourceForm: Display)
       mask: (BiColorForm color: 11);
       copyBits;
       destForm: Display;
       sourceForm: aForm;
       copyBits
```

extracts the area with color 11 from the screen to `aForm`, and blacks out the rest when `aForm` is copied back to the screen.

When the source form is `nil`, if the destination is a `BiColorForm`, then bits in the mask form will be tiled over the destination. If the destination object has multiple bitmaps then 1 bits in the mask form will take on the foreground color of the mask form, 0 bits the background color, and the colors will be tiled over the destination area. For example,

```pascal
(BitBlt destForm: Display sourceForm: nil)
       mask: (BiColorForm color: 4);
       destRect: (0 @ 0 extent: 100 @ 100);
       copyBits
```

paints a red rectangle in the top left corner of the screen.
## Halftone (Mask)

A halftone, or mask, is a Form which combines with the source form to create the effect of gray tone. This mask Form is restricted to have a width and height of 16. To copy a mask over a larger area, BitBlt repeatedly applies (tiles) the content throughout the entire affected area. Since a bit value 1 represents white and 0 represents black, a white mask form contains all 1 bits while a black one contains all 0 bits. There are four possible ways to combine source and mask forms; evaluate each of the following expressions to see the results:

```
"no source, no halftone (displays solid white)"
(BitBlt destForm: Display sourceForm: nil)
  destRect: (0 @ 0 extent: 100 @ 100);
  copyBits
```

```
"halftone only (gives halftone tiling)"
(BitBlt destForm: Display sourceForm: nil)
  mask: Form gray;
  destRect: (0 @ 0 extent: 100 @ 100);
  copyBits
```

In this case, the message Form gray returns a prebuilt mask form which yields a gray tone effect. Other prebuilt mask forms can be obtained by sending the message black, darkGray, gray, lightGray, or white to class Form.

```
"source only (gives source bits)"
(BitBlt destForm: Display sourceForm:
  (Form new width: 100 height: 100 initialByte: 16rF0))
  copyBits
```

Where the initialByte argument, 16rF0, specifies the initial value for all bytes in the new Form. The result will be white and black vertical strips (each four pixels wide).

```
"both specified (bits in mask are ANDed with bit in source)"
(BitBlt destForm: Display sourceForm:
  (Form new width: 100 height: 100 initialByte: 16rF0))
  mask: Form darkGray;
  copyBits
```

When a mask having colors other than black and white is desired, a BiColorForm mask should be used to control which color is painted on the screen or on a ColorForm. The following messages are often used to create such a mask form:

**BiColorForm color: aColor**
Creates a 16 x 16 BiColorForm with all 1 bits, with aColor as its foreground color and 0 (black) as its background color.