You are on page 1of 295

A Little Smalltalk

Timothy Budd
Oregon State University
..
TT
Addison-Wesley Publishing Company
Reading, Massachusetts • Menlo Park, California
Don Mills, Ontario • Wokingham, England • Amsterdam • Sydney
Singapore • Tokyo • Madrid • Bogota • Santiago • San Juan
We would like to thanks Tim Budd and his publisher to let us have this book
available.
A Little Smalltalk has been scanned and prepared by A. Leinhard, L. Reenggli
and S. Ducasse. Note that the book does not have been OCRed because of lack
of time. If you are in the mood to do it, please us.
You have to pay attention about the following copyright notice. Note that it was
not possible to add this notice to all the pages as footnote for obvious technical
reasons.
Pearson Education, Inc. reserves the right to take any appropriate action if you have used our
intellectual property in violation of any of the requirements set forth in this permissions letter.is
hereby granted permission to use the material indicated in the following acknowledgement. This
acknowledgement must be carried as a footnote on every page that contains material from our
book:LITTLE SMALLTALK by Timothy Budd. Reproduced by permission of Pearson Education,
Inc. © 1987 Pearson Education, Inc. All rights reserved. This material may only be used in the
f ol l owi ng manner: To post t he ent i re book t o t he f ol l owi ng websi t e
http://www.iam.unibe.ch/~ducasse/webpages/freebooks.html. This permission is only given on
the understanding that access to the material on your website is free to users. You agree to
p l a c e a l i n k f r o m t h i s b o o k t o
http://www.aw.com/catalog/academic/discipline/1,4094,69948,00.html. Permission to post this
material to your website will expire on January 1st, 2007. If you wish to continue to post the
material after that date you will need to reapply for permission referencing this letter. Permission
is also given for the entire book to be included on a CD-ROM. This CD-ROM permission is only
given on the understanding that it will not be sold for profit. Any monies received from the CD-
ROM must only be used to cover expenses in its production.This permission is non-exclusive and
applies solely to publication in ONE CD-ROM EDITION and posted to the website found at
http://www.iam.unibe.ch/~ducasse/webpages/freebooks.html in the following language(s) and
territory:Language(s): English Territory: World
NOTE: This permission does not allow the reproduction of any material copyrighted in or credited
to the name of any person or entity other than the publisher named above. The publisher named
above disclaims all liability in connection with your use of such material without proper consent.
"\ \ \
\
'> :\
Library of Congress Cataloging-in-Publication Data
Budd, Timothy.
A Little SmaIItalk.
Includes index.
1. Electronic digital computers-Programming.
2. Little SmaIItalk (Computer system) I. Title.
QA76.6.B835 1987 005.26 86-25904
ISBN 0-201-10698-1
Reprinted with corrections April, 1987
Copyright © 1987 by Addison-Wesley Publishing Company, Inc.
All rights reserved. No part of this publication may be
reproduced, stored in a retrieval system, or transmitted in any
form or by any means, electronic, mechanical, photocopying,
recording, or otherwise, without prior written permission of
the publisher. Printed in the United States of America.
Published simultaneously in Canada.
BCDEFGHIJ-DO-8987
Preface
The Little Smalltalk System: Some History
In the spring of 1984 I taught a course in programming languages at the
University of Arizona. While preparing lectures for that course, I became
interested in the concept of object-oriented programming and, in partic-
ular, the way in which the object-oriented paradigm changed the pro-
grammers approach to problem solving. During that term and the
following summer I gathered as much material as I could about object-
oriented programming, especially items relating to the Smalltalk-80 pro-
gramming systemI developed at the Xerox Palo Alto Research Center (Xe-
rox PARC). However, I continued to be frustrated by my inability to gain
experience in writing and using Smalltalk programs.
At that time the only Smalltalk system I was aware of was the original
system running on the Dorado, an expensive machine not available (at
that time) outside of Xerox PARCo The facilities available to me consisted
of a VAX-780 running Unix
2
and conventional ASCII terminals. Thus, it
appeared that my chances of running the Xerox Smalltalk-80 system, in
the near term, were quite slim; therefore, a number of students and I
decided in the summer of 1984 to create our own Smalltalk system.
In the fall of 1984 a dozen students and I created the Little Smalltalk
system as part of a graduate level seminar on programming language
implementation. From the outset, our goals were much less ambitious
than those of the original developers of the Smalltalk-80 system. While we
appreciated the importance of the innovative concepts in programming
environments and graphics pioneered by the Xerox group, we were pain-
fully aware of our own limitations, both in manpower and in facilities.
Our goals, in order of importance, were:
o The new system should support a language that is as close as possible
to the published Smalltalk-80 description (Goldberg and Robson 83).
o The system should run under Unix using only conventional terminals.
o The system should be written in C and be as portable as possible.
o The system should be small. In particular, it should work on 16-bit
machines with separate instruction and data spaces, but preferably
even on those machines without this feature.
I. Smalltalk-80 is a trademark of the Xerox Corporation.
2. Unix is a trademark of AT&T Bell Laboratories.
v
vi
\
\
\
.,
.'
:}o:
Preface
In hindsight, we seem to have fulfilled our goals rather well. The lan-
guage accepted by the Little Smalltalk system is close enough to that of
the Smalltalk-80 programming system that users seem to have little dif-
ficulty (at least with the language) in moving from one system to the other.
The system has proved to be extremely portable: it has been transported
to a dozen varieties of Unix running on many different machines. Over
200 sites now use the Little Smalltalk system.
About A Little Smalltalk
This book is divided into two parts. The first section describes the language
of the Little Smalltalk system. Although most readers probably will have
had some prior exposure to at least one other programming language
before encountering Smalltalk, the text makes no assumptions about back-
ground. Most upper division undergraduate or graduate level students
should be able to understand the material in this first section. This first
part of the text can be used alone.
The second part of the book describes the actual implementation of
the Little Smalltalksystem. This section requires the reader to have a much
greater background in computer science. Since Little Smalltalk is written
in C, at least a rudimentary knowledge of that language is required. Agood
background in data structures is also valuable. The reader will find it
desirable, although not strictly necessary, to have had some introduction
to compiler construction for a conventional language, such as Pascal.
Acknowledgments
I am, of course, most grateful to the students in the graduate seminar at
the University of Arizona where the Little Smalltalk system was developed.
The many heated discussions and insightful ideas generated were most
enjoyable and stimulating. Participants in that seminar were Mike Ben-
hase, Nick Buchholz, Dave Burns, John Cabral, Clayton Curtis, Roger
Hayes, Tom Hicks, Rob McConeghy, Kelvin Nilsen, May Lee Noah, Sean
O'Malley, and Dennis Vadner. This text grew out of notes developed for
that course, and includes many ideas contributed by the participants. In
particular I wish to thank Dave Burns for the original versions of the
simulation described in Chapter 7 and Mike Benhase and Dennis Vadner
for their work on processes and the dining philosophers solution presented
in Chapter 10.
Discussions with many people have yielded insights or examples that
eventually found their way into this book. I wish to thank, in particular,
Jane Cameron, Chris Fraser, Ralph Griswold, Paul Klint, Gary Levin, and
Dave Robson.
Irv Elshoff provided valuable assistance by trying to learn Smalltalk
from an early manuscript and by making many useful and detailed com-
ments on the text.
1 ~
\
\
\
~ \
"
Preface. vii
J. A. Davis from Iowa State University, Paul Klint from the CWI, David
Robson from Xerox Palo Alto Research Center, and. Frances Van Scoy
from West Virginia University provided careful and detailed comments on
earlier drafts of the book.
Charlie Allen at Purdue, Jan Gray at Waterloo and Charles Hayden at
AT&T were early non-Arizona users of Little Smalltalk and were extremely
helpful in finding bugs in the earlier distributions.
I wish to thank Ralph Griswold, Dave Hanson, and Chris Fraser, all
chairmen of the computer science department at the University of Arizona
at various times in the last five years, for helping to make the department
such a pleasant place to work. Finally I wish to thank Paul Vitanyi and
Lambert Meertens for providing me with the chance to work at the Cen-
trum voor Wiskunde en Informatica in Amsterdam for the year between
my time in Arizona and my move to Oregon, and for permitting me to
finish work on the book while there.
Obtaining the Little Smalltalk System
The Little Smalltalk system can be obtained directly from the author. The
system is distributed on 9-track tapes in tar format (the standard unix
distribution format). The distribution tape includes all sources and on-line
documentation for the system. For further information on the distribution,
including cost, write to the following address:
Smalltalk Distribution
Department of Computer Science
Oregon State University
Corvallis, Oregon
97331
USA
\
>-.
\-
.':':.
\
.\
':-
)
Table of Contents
PAR T
--ONE
The
Language 1
=CHAPTER 1
Basics................................................. .................................... 3
Objects, Classes, and Inheritance 5
History, Background Reading............................................................... 9
This chapter introduces the basic concepts of the Smalltalk language;
namely object, method, class, inheritance and overriding.
=CHAPTER 2
Syntax 12
Literal Constants 13
Identifiers.............................................................................................. 14
Messages............................................................................................... 15
Getting Started 17
Finding Out About Objects 18
ix
").
\
~ ~
\,
..~ > ~ \
x Contents
Blocks 19
Comments and Continuations 20
This chapter introduces the syntax for literal objects (such as numbers)
and the syntax for messages. It explains how to use the Little Smalltalk
system to evaluate expressions typed in directly at the keyboard and
how to use a few simple messages to discover information about dif-
ferent types of objects.
CHAPTER 3
Basic Classes......................................................................... 22
Basic Objects 23
Collections 24
Control Structures 28
Class Management............................................................................... 30
Abstract Superclasses 32
The basic classes included in the Little Smalltalk standard library are
explained in this chapter.
CHAPTER 4
Class Definition.................................................................... 34
An Illustrative .Example 37
Processing a Class Definition 39
This chapter introduces the syntax used for defining classes. An ex-
ample class definition is presented.
CHAPTER 5
A Simple Application........................................................... 42
Saving Environments 49
This chapter illustrates the development of a simple application in
Smalltalk and describes how environments can be saved and restored.
-CHAPTER 6
Contents xi
Primitives, Cascades, and Coercions 51
Cascades................................................................................................ 52
Primitives 53
Numbers 54
This chapter introduces the syntax for cascaded expressions and de-
scribes the notion of primitive expressions. It illustrates the use of
primitives by showing how primitives are used to produce the correct
results for mixed mode arithmetic operations.
::=CHAPTER 7
A Simulation......................................................................... 59
The Ice Cream Store Simulation 60
Further Reading 72
This chapter presents a simple simulation of an ice cream store, illus-
trating the ease with which simulations can be described in Smalltalk.
CHAPTER 8
Generators 74
Filters 79
Goal-Directed Evaluation 81
Operatkms on Generators 84
Further Reading 91
This chapter introduces the concept of generators and shows how
generators can be used in the solution of problems requiring goal-
directed evaluation.
=CHAPTER 9
Graphics................................................................................ 95
Character Graphics 97
Line Graphics : 102
Bit-Mapped Graphics , - 106
xii
"\:.
\
\
\ ~
~ \ : ~ ~ \
Contents
Although graphics are not fundamental to Little Smalltalk in the same
way that they are an intrinsic part of the Smalltalk-80 system, it is still
possible to describe some graphics functions using the language. This
chapter details three types of approaches to graphics.
-CHAPTER 10
Processes 109
Semaphores 114
Monitors 115
The Dining Philosophers Problem 116
Further Reading 122
This chapter introduces the concepts of processes and semaphores. It
illustrates these concepts using the dining philosophers problem.
PAR T
TWO
The
Implementation 125
CHAPTER 11
Implementation Overview 127
Identifier Typelessness 129
Unscoped Lifetimes 129
An Interactive System 131
A Multi-Processing Language ~ 132
System Overview 135
This chapter describes the features that make an interpreter for the
Smalltalk language different from, say, a Pascal compiler. Provides a
high-level description of the major components in the Little Smalltalk
system.
CHAPTER 12
Contents xiii
1he Representation of Objects 137
Special Objects 141
Memory Management 144
Optimizations 148
The internal representation of objects in the Little Smalltalk system is
described in this chapter, which also overviews the memory manage-
ment algorithms. The chapter ends with a discussion of several optim-
izations used to improve the speed of the Little Smalltalk system.
CHAPTER 13
Bytecodes 150
The Representation of Methods : 156
Optimizations - , 157
Dynamic Optimizations 159
The techniques used to represent methods internally in the Little Small-
talk system are described in this chapter.
-CHAPTER 14
The Process Manager 161
The Driver 170
The Class Parser 172
This chapter presents a more detailed view of the central component
of the Little Smalltalk system, the process manager. It then goes on to
describe the driver, the process that reads commands from the user
terminal and schedules them for execution. The chapter ends by de-
scribing the class parser and the internal representation of classes.
CHAPTER 15
The Interpreter 176
Push Opcodes 179
Pop Opcodes 182
xiv
"
.'
Contents
Message-Sending Opcodes 182
Block Creation 185
Special Instructions 186
The Courier 189
The Primitive Handler 189
Blocks 190
This chapter describes the actions of the interpreter and the courier
in executing bytecodes and passing messages. It ends by describing
the primitive handler and the manipulation of special objects.
References 193
An annotated bibliographyof references related to the Little Smalltalk
system.
Projects 198
Appendices
APPENDIX 1
Running Little Smalltalk 209
Describes how to run the Little Smalltalk system. Lists the various
options available.
APPENDIX 2
Syntax Charts 213
Presents syntax charts describing the language accepted by the Little
Smalltalk system.
APPENDIX 3
Class Descriptions 225
Presents descriptions of the various messages to which the classes in
the standard library will respond.
APPENDIX 4
Contents xv
\.
Primitives 261
Gives the meanings of the various primitive numbers.
APPENDIX 5
Differences Between Little Smalltalk
and the Smalltalk-80
Programming System 272
Describes the differences between Little Smalltalk and the Xerox
Smalltalk-80 systems.
-
-
\
.\.
@) V !\ /- 3«« <<««« <««<
))))))))))))))))))))))))))))))))))))))))))))§ § f ! 2»»»>>>>>>>>>>>>
{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@ §: ®'0 j / / / / / / / / / / / / / / / /
f :::: ;;; 2'-""""""""""""""""
" " " '"" '"" " '"'"'"'"" " " " @, j @"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{i
///////////////// §: 0 j @}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}.
/\ "'/'--.
::tf:: V !\ '" / §({«««((((((((((((((((
»»»»»»»»»»§ j §»»»»»»»»»»»»»»»»»»»»)
:/ V /\ ;8.) ::tf:: : \S) V /\ "./
r / '" V !\ :+::::::::: ::: -Ie: @: V /\ '" / 8
t: / '" V i\ (8', ::It:: :+:::::: :::»»»»»»»»»>:::: iC @i V t\ '" -.-
/ " ,; 1\ ® 'lj, S :: ::: : ,V II " /
/ " 8 '8' :::: ! §// / / / / / / / / / / / / ////2 f :::: ;;; ::':0
;;; ! ! §'-"""""""""""""""",,
2
f ! §§
3 ;;; :::: ! § f :::: ;;; ;\
: ;;; :::: § §}}}}}}}}}})}})}}}}}}}}}}}}}}}}}}}}}}}}}}}}})}}}§: :::: @: ;;; S
€ j j: ® : @0 j §
j : : § ! y j s
(« (««(««((((((((((««(((((((((((((«((::: ::::::.j( % \S) \'j '" / :: < <<<<<<<<<<< << <<<<
§ f ! 8 »»»»»»»»»
§; ! §// / / / / / / / / / / / / / / /
®0 '"" " " " '"" " '"'"" '"'"'"" " "
""'""""""'"'"'"'"""""'" '" /
:::: :::::: of{ % ,v, ' \ '" /
/ / / / / / / / / / / / / / / / / +: ::tf:: ;,s. !\ '" /
-:: __ , v !\ " / _
.- \S} V ;\ "
:::::: iC @j V /\ '" / §«««««««««««{«{«««««««I
:::: ::tf:: V (\ '" /
>>>»»>>>»»>>> > V (\ ::- »»»»»»»»»»»»»»»»»»»»
'0 : : § :::::: +: ::tf:: '" /
/ '" V /\ ::It:::+::::::;:; ::: % V 1\ " /
_ V /\ (8) :+:::::: ;:;»»»»»»»»»>=:;: :::::: iC ::It:: 8 \f /\ '" /
// V /\ :::::::::: ::: ::::::: @,:\Vfl\"'/
- / '" \j /\ : § §// / / / / / / / / / / / / / / / / § §: ::It:: '2. V /\ '" /
j 0 : §'"'"" '"'"'"" '"'"'"'"" '"'"" " ,,"'§ is) j
:::: ! §{{{{{{{{{{f{f{{{f{{{{{{{{{{{f{{{f{{{{{{f{{f{{{{{€ §: !
j 0 : §: /
0 ;\ :::: ! § i ! ;;;
0 $, : : § §: 0 ;\ '" /
"
V
j\ ::u::
*
'-""
,.
*
'-""
,.
"
\!
it::
.......-
,.
v
;\
"S?'
*
.......-
,.
::u::
'-"'
...
"
v ..

*
'-"'
...
::u::
*
--
...
"
v
,.
--
...

::u::
* -
...
"
v /\
*
-
it:: -
,r\

*
--
...
\,/
--
--
...
"

/\


* -
,.
"
%:
*
-
...
......- ...
V
:..

*
......... ...
"
:\
%:
*
---
...
\/
1\
---
...
"
1
. ;-.,

*
--
...
.... /
!\
4
*
--
...
v
-- ....................
.......- ...
\/ !\

*
*
----
...
"
* ----
...
\.j
!\
-
'*
*
.......- ...
.......- ...
"-- \! /\

*
*
--
...
*
--
...
"
\/
j\
--
...
*
--
...
"
V
1\
it:: ----
...
:\
." .
* ----
'--'"
...
"
V
/\
*
'-"
...

*
'--'"
...
'-
\/ !\
%:
>1-
.......- ...
:««««««««««««««({{{««(
))))))))))))))))))))))}}}}))))
{{{{{ {{ {{{{{{{{{ {{ {{ {{{{{{{{{ {{{{{{{{{{{
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
, "-'-,, '-',""'. "'. "'.'" "'-, "'. ''-, "'. '-
'// / /////////////
:««««««««

"'.
y ...

it::
>t-
.......-
\/ I .....
% *
.......-
"'.
j\
..
*
.......-
.......-
"-.
:'.
%
*
.......-
V !\

.......-
it::
*
--
"'.
\/
;\,
*
-- '2.

--
' .........
*
'--'"
\/ /\
%:
*
--
"'. /\ -- \/
%
* --
*
--
"
\j
/\

--
*
-
...... \./ /\

%:
*
----
-
"'.
\/ !\

%
*
-
!\ "It:
*
-
"'.
\j

*
'--'"
\j ;\

::tl::
*
.......-
............
.......-
V
!\
%
* ---
"
.";::-".
'#:
*
.......-
\j /\
.......-
*
.......-
"'.
\1 /\

*
......-
-- --
"--

*
......-
\/ !\
---,.
-
-,,-
'#:
* -
"
V
;\
-:0
*
- ,
:tl:: ...
--
«({{««««««««({{{{«««««
»»»»»»»»»»»»»»»»»»
{{f{{{{{ {-[ {HI{ f{ {-[ {H{{ {{ {{ {{ {{ {{ {{{{{ {
H}}}} n }l-} }}}}}}}l} an}}}}}}}}} n}}}} J
"'. "'." "'. ----,.'" "'. -"" "'.-', "'. "', "'. '-" "-
/ ///.,.....-//// /,//'////
:::: <;. ...-:.: <::. .,::: ... <"::: .... .<: <:: <::
,> :-> :;> :> :> ::> ::> :>
/
PAR T
ONE
The
Language
\
.\
\
~ \ - -
»»»»»>
//////////
"'-"'-"'-"'-"'-"'-"'-"'-"'-"
{{{{{{{{{{{H{{{{{{{{{{{{i
.}}}}}}}}}}}}}}}}}}}}}}}}1:
«({({««««««««(,
»»»»»
//////////
""'-"'-"'-"'-"'-"'-"'-"'-"
:{{{{{{{{{{{{{{{{{{{{{{{{{
HHHH}HHn}}}}}}}}}}}
((((((((({((\
)))))))))))))))))))))))
CHAPTER
1
Basics
3
4
The Language
The traditional model describing the behavior of a computer executing a
program is the process-state, or "pigeon-hole" model. In this view the
computer is a data manager, following some pattern of instructions, wan-
dering through memory, pulling values out of various slots (memory ad-
dresses), transforming them in some manner, and pushing the results back
into other slots. By examining the values in the slots one can determine
the state of the machine or the results produced by a computation. While
this may be a more or less accurate picture of what takes place in a
computer, it does little to help us understand how to solve problems using
the computer, and it is certainly not the way most people (pigeons and
postmen excepted) go about solving problems.
Let us examine a realistic situation and then ask how we might make
a computer more closely model the methods people use for solving prob-
lems in everyday life. Suppose I wish to send some flowers to my grand-
mother for her birthday. She lives is a city many miles away. The task is
easy enough to do; I merely go to a local florist, describe the kinds and
number of flowers I want sent and I can be assured that they will be au-
tomatically delivered. If I investigate, I would probably discover that my
florist sends a message describing my order to another florist in my grand-
mother's city. That florist then makes up the arrangement and delivers the
flowers. I might inquire further tofind out howtheflorist in mygrandmother's
city obtains the flowers and find, perhaps, that they are obtained in bulk in
the morning from a flower wholesaler. If I persist, I might even be able to
follow the chain all the way back to the farmer who grows the flowers, and
discover what requests were made by each member of the chain in order
to solicit the desired outcome from the next.
The important point, however, is that I do not need, indeed most of
the time do not want, to know how my simple directive "send flowers to
my grandmother" is going to be carried out. In real life we call this process
Udelegation of authority." In computer science it is called l'abstraction" or
"information hiding.'1 At the heart, these terms amount to the same thing.
There is a resource (a florist, a file server) that I wish to use. In order to
communicate, I must know the commands to which the resource will
respond (send flowers to my grandmother, return a copy of the file named
"chapter!"). Most likely the steps the resource must take to respond to my
request are much more complex than I realize, but there is no reason for
me to know the details of how my directive is implemented as long as the
response (the delivery of the flowers, receiving a copy of my file) is well
defined and predictable.
The object-oriented model of problem solving views the computer in
much the same fashion as just described. Indeed many people who have
no training in computer science and no idea how a computer works find
the object-oriented model of problem solving quite natural. Surprisingly,
however, many people who have a traditional background in computer
Basics 5
programming initially think there is something strange about the object-
oriented view. The notion that "7" is an object and" +II is a request for an
addition, may at first seem strange. But soon, the uniformity, power, and
flexibility the object-message metaphor bring to problem solving makes this
interpretation seem natural.
The Smalltalk universe is inhabited by objects. In my flo\ver example, I
amanobject and the flower shop (or the florist in it) is another object. Actions
are initiated by sending requests (or 111,essages) between objects. I trans-
mitted the request "send flowers to my grandmother" to the florist-object.
The reaction of the receiver of my message is to execute some sequence of
actions, or method, to satisfy my request. Maybe the receiver can imme-
diately satisfy my request. On the other hand, in order to meet my needs,
the receiver may have to transmit other messages to yet more objects (for
example, the message my florist sends to the florist in mygrandmother's city,
or a command to a disk drive). In addition, there is an explicit response
(a receipt, for example, or a result code) returned directly back to me. Dan
Ingalls describes the Smalltalk philosophy (Byte 81):
Instead of a bit-grinding processor raping and plundering data struc-
tures, we have a universe of well-behaved objects that courteously ask
each other to carry out their various desires.
Such anthropomorphic viewpoints are common among Smalltalk pro-
grammers. In subsequent chapters we will see how the Smalltalk language
embodies this object-oriented view of programming. By describing the
solution of several problems in Smalltalk, we hope to show how the object-
oriented model aids in the creation of large software systems and assists
in the solution of many problems using the computer.
Objects, Classes, and Inheritance
In Smalltalk, everything is an object. There is no way to create, within the
language, an entity that is not an object. Among major computer languages
this uniformity in Smalltalk is rivaled perhaps only by LISP, and, as with
LISP, the uniformity creates both the simplicity and power of the language.
An object possesses several characteristics (Figure 1.1). Every object
contains a small amount of memory, accessible to only that object. That
is, no object can read or modify memory values in another object. Of
course, since everything in the system must be an object, an object's mem-
ory can contain only other objects. We will discuss this in more detail
later.
"\
\ \. \
~ \ ~ : } .-;;,
.'
6
Figure 1.1 D A typical object
The Language
description
class pointer
a class
I - - - ~ Methods
description
memory 1-----------1
object
pointers
other
objects
All actions in the Smalltalk system are produced by passing messages.
A message is a request for an object to perform some operation, and it
can contain certain argument values to be used in conjunction with the
execution of the requested operation. There are two different ways to view
this message passing operation. The first is simply that message passing
corresponds to a subroutine call in a conventional procedural language,
such as Pascal. This is true in that the actions of the sender ate suspended
while the receiver produces a result. The result is then returned to the
sender, who continues execution from the point of call. Messages can be
created dynamically at run-time, however, and the relationship between
the sender and receiver of a message is typically much more fluid than the
static relationship between a caller and the callee in a conventional pro-
gramming language.
In the real world every object is individualistic; however, each also
possesses common characteristics with other, similar, objects. In a bushel
of apples, for example, each apple is distinct from all others. Yet certain
statements can be made about all the apples; for example, they will all
Basics
\
~ . ~ -
7
smell and taste' a certain way, can all be used to bake pies in a similar
manner, and so on. This process is called classification. That is, we can
view an apple as an individual item or as an instance of a larger class (or
category) of objects. let us denote the class of all apples by Apple, the
capital letter and the boldface type serving to denote the fact that we are
talking about a class, and not an individuaL
Instances of class Orange are in many respects different from apples,
and thus deserve their own category. But they also share many common
characteristics with apples. Thus we can create a new class, Fruit, to be
used when we wish to describe characteristics common to both apples and
oranges. The Class Fruit encompasses both the classes Apple and Orange.
Thus we say that Fruit is a superclass of Apple and Orange, and that
Apple and Orange are in turn subclasses of Fruit.
Finally, we can take this analysis even one step further, making Fruit
a subclass of a more universal category, which we can call Object. Thus
we have a hierarchy of categories for objects, extending fro'm the basic
class Object, of which everything is a member, down through more and
more specific classes until we reach the individual object itself.
The same situation holds regarding entities in Smalltalk. That is, every
object is a member of some class. With the exception of class Object, that
class will, in turn, be a subclass of some larger class, which in turn may
be part of another class, up to one single class of which every object is a
member. There is a natural tree structure (Figure 1.2) that illustrates this
class-subclass hierarchy. As we have been doing, we will denote class
names by capitalizing the first letter and denote object names without
using the capital. So, for example, the number 7 is an instance of the class
Integer, as is the number 8. Although 7 and 8 are distinct objects, they
share some characteristics by virtue of their being instances of the same
class. For example, both 7 and 8 will respond to the message "+" with an
Figure 1.2 0 The tree structure of the class-subclass Hierarchy
Object
/ ~
FrLiit Vegetable
/\ /\
Apple Orange Turnip Carrot
'. \ \
'(
.} ~ } :.." >
8 The Language
integer argument by performing integer addition. Integer is a subclass of
a larger class, Number. There are other subclasses of Number, for ex-
ample, Float, of which values such as 3.1415926 are instances. Number
is a subclass of Magnitude (a class to be discussed later) which finally is
a subclass of Object.
The behavior of an object in response to a specific message is dictated
by that objecfs class. For example, 7 and 8 will respond to the message
If + " in the same fashion, because they are both instances of class Integer.
The list of statements that define how an instance of some class will re-
spond to a message is called the method for that message. For example,
in class Integer there is a method associated with the message" +." The
entire set of messages associated with a class is called the protocol for that
class. Class Integer contains in its protocol, for example, messages for +,
-, *, and so on. In Smalltalk, a protocol is provided as part of a class
definition. The syntax for class definitions will be described in a later sec-
tion. It is not possible to provide a method for an individual object; rather
every object must be associated with some class, and the behavior of the
object in response to messages will be dictated by the methods associated
with that class.
. If an object is an instance of a particular class, it is clear how methods
associated with that class will be used, but what about methods associated
with superclasses? The answer is that any method associated with a su-
perclass is inherited by a class. An example will help clarify this concept.
When sent to a number, the message exp means "return the value of e
(approximately 2.71828.. ) raised to your value." Thus 2 exp yields e
2
, or
approximately 7.38906. Now the class description for Integer does not
provide a method for the message exp, so that when the Little Smalltalk
system tries to find an associated method for the message exp in the class
Integer protocol, it does not find one. So the Little Smalltalk system next
examines the protocol associated with the immediate superclass of Inte-
ger, namely Number. There, in the protocol for Number, it finds a method
and executes it. Thus, we say that the method for exp is inherited by the
class Integer from the class Number.
In Number, the method associated with the message exp is as follows:
i self asFloat exp
We will explain the syntax in more detail later; for the moment we can
translate this as "produce an instance of Float with your value (self asFloat)
and send that object the message exp asking for e raised to its value. Return
(the up arrow i indicates returning a value) the response to that message."
Thus the message asFloat is passed to the original integer, say, 2. The
method associated with this message is executed, resulting in a floating
point value, 2.0. The message exp is then passed to this value. This is the
same message that was originally passed to the integer 2, only now the
class of the receiver is Float, not Integer.
Basics
Figure 1.3 0 The class structure of numbers
Object
I
Magnitude
/ ~
Number Char
/""
Integer Float
9
\.
Figure 1.3 shows the hierarchy representing several classes, including
numbers. As we have seen, a method for the message exp is defined in
both the classes Number and Float. Search for a method begins with the
class of an object and then proceeds through the various superclasses
(along the superclass chain), as necessary. If a floating point value is given
the message exp it will execute the method in class Float, not the method
in class Number. Thus the method for exp in Float is said to override the
method in class Number.
Classes such as Number and Magnitude, which usually do not have
any explicit instances, are known as abstract superclasses. Abstract super-
classes are important in insuring that instances of different classes, such
as integers and floating point numbers, will respond in a similar manner
in common situations. An addition, by eliminating the need to duplicate
the methods for the messages in the superclass, they reduce the size of
the descriptions needed to obtain a desired behavior.
History, Background Reading
The concepts relating to object-oriented programming found in Smalltalk
are the products of a long process of language development and evolution.
The fundamental notions of objects, messages, and classes came from the
language Simula (Birtwistle 73). While Simula allowed users to create
object-oriented systems, within a class the response to a message (the
Simula equivalent of a method) was still expressed in the standard ALGOL
data/procedure-oriented fashion.
Within the ALGOL family of languages, the concept of classes led to
the development of the notion of modules and abstract data types (Shaw
80), the support for which was a fundamental goal in several languages
such as Euclid, CLU, Modula, and Ada.
subclass
superclass
inheritance
overriding
abstract superclass
10
\ \
"\
"\
,
.'
:..... .\
The Language
While the object-oriented philosophy was slowly gaining acceptance
in the programming language world, similar ideas were gaining acceptance
in the architecture community (Pinnow 82). Similarly, in operating sys-
tems design the notion of independent computations that interact with
each other solely by exchanging messages was finding advocates (Wulf 74),
(Almes 85). Such a view is natural and convenient when then the com-
putations may physically be executing on distributed processors.
DIrect ancestors of Smalltalk include the Flex system (Kay 69), Small-
taIk-72 (Goldberg 76), and Smalltalk-76 (Ingalls 78). The Smalltalk lan-
guages were all produced as part of the Dynabook project initiated by Alan
Kay in the Learning Research Group at the Xerox Palo Alto Research
Center. The evolution of the language as traced in these documents shows
the object-oriented model slowly being expanded to include more and more
language concepts. For example, in Smalltalk-72, numbers and control
structures are treated as objects, in contrast to Simula, but classes are still
a special form. In Smalltalk-76, class descriptions are represented as ob-
jects, and the object-oriented point of view is extended to the program-
ming interface. This interface becomes almost completely described in
object-oriented form in the Smalltalk-80 programming environment (Gold-
berg,83).
The object-oriented view of programing has also influenced other com-
puter languages, most notably in the notion of actors (Hewit 73) and flavors
(Weinreb 80) in Lisp, and in the development of languages for animation
and graphics (Reynolds 82). The development of actors in Lisp paralleled
the development of Smalltalk, and the two languages had an influence on
each other.
Koved and LaLonde present overviews describing the object-oriented
viewpoint in various guises (Koved 84) (LaLonde 84). A number of papers
describing various aspects of the Smalltalk-80 system were included in a
special issue of the magazine Byte (Byte 81).
EXERCISES
1. Define the following terms:
object
message
receiver
method
protocol
class
2. Give an example of a hierarchy from everyday life. List properties that
can be found at each level, and distinguish those that are found in
lower levels but not higher levels.
Basics 11
3. Read about the Simula class mechanism (DaW 72) (BirtwistIe 73).
Compare and contrast this with the Smalltalk class mechanism.
4. In the real world, objects are often classified in orthogonal ways, rather
than in the tree-like hierarchy of Smalltalk. For example, the bald eagle
and the condor are both birds of prey, but one is a North American
bird and the other a South American bird. The robin is also a North
American bird but is not a bird of prey. These two distinguishing
characteristics are orthogonal in that neither can logically be said to
be a superset of the other. Thus. forcing the classification into a tree-
like structure is either unnatural, inefficient, or both.
How might Smalltalk objects be classified in orthogonal ways? What
problems does this introduce for the inheritance mechanism? How
might these problems be overcome?
Bird Not a Bird
of of
Prey Prey
North
American
South
American
Bald
Eagle
Condor
Robin
?
'.
\
'\
" ~ \
.:.:
/\
!\
/ / //' / /
///////////
:{{{{{{{{{{{{{{{{H{{{{{{{
r}}} }}}} }}}}}l}}I}}}} H}}}
;{{{({{««««««««:
CHAPTER
2
Syntax
12
Syntax 13
This chapter will describe the way in which objects are represented and
manipulated in Little Smalltalk. As we noted in Chapter 1, everything in
Smalltalk is an object. The discussion of syntax begins with a description
of how objects are represented.
Literal Constants
Some objects, literal objects, are distinguished by the fact that their name
uniquely identifies the object, independent of context, and by the fact that
they do not have to be declared prior to being used. For example, the
symbol 7, no matter where it appears, always denotes the same object. In
Algol-like languages a symbol such as 7 conventionally denotes a "value"
rather than an identifier. In Smalltalk this distinction is much less distinct.
All entities, including numbers, are objects, and objects are characterized
by the messages they accept and their response to them. Thus 7 denotes
an object in the same way that an identifier, such as x (in the proper
context), might denote an object.
Numbers are perhaps the most common literal objects. There are two
classes of numbers that can be written as literal objects, namely integer
and floating point values. Numbers respond to a variety of arithmetic mes-
sages (inherited from class Number) and relational messages (inherited
from class Magnitude). An instance of class Integer consists of an optional
sign followed by any number of digits. A Float consists of an integer
followed by a period (the radix point) and another unsigned integer (the
fractional part) and/or the lettere and a signed integer (the exponent part).
Any number can be preceded by a base, which is a positive integer followed
by the letter t. For bases greater than 10 the letters A through Z are in-
terpreted as digit characters. Examples of numbers are:
7
16rFF
- 3.1415926
2e32
2.4e-32
15rC.ABC
The use of a base is merely for the sake of convenience and appearance.
The number 16rFF is the same as the number 10r255, or just 255.
The class Char provides capabilities for dealing with character values.
Characters are distinct from numbers. Since characters possess an order-
ing, given by a collating sequence, they can be compared and hence are a
subclass of class Magnitude. Acharacter is written as a dollar sign followed
by the character symbol. The following are example instances of this class:
14
:.
\ \ "\
~ \
., .,.
.'-:
The Language
$A
$7
$
$$
An instance of class String is represented by a sequence of characters
between single quote marks. Embedding a quote mark within a string
requires two adjacent quote marks. A string is similar to an array; in fact
the class String is a subclass of ArrayedCollection, as is the class Array.
Both strings and arrays can be catenated together to form larger strings
by using the comma (,) operator. Examples of strings are:
'a String
l
!a String with an II embedded quote mark!
An Array is written as a pound sign (#) followed by a list of array
elements in parentheses. The elements in the array list are literal objects
(numbers or symbols), strings, or other arrays. Within the array list the
leading pound sign on symbols and arrays can be eliminated. Examples
of Arrays are:
#(this is an array of symbols)
#(12 label (another array»
Arrays and strings use the messages at: and at:put: to select and modify
particular elements in their collection.
The class Symbol is another literal class. A symbol is written as a
pound sign (#) followed by any sequence of characters. Spaces between
characters are not allowed. Unlike a string (which is also a sequence of
characters) a symbol cannot be broken into smaller parts. Furthermore
the same sequence of letters will always denote the same object. Unlike
numbers, characters, or strings, symbols do not possess an ordering and
cannot be compared (except for, of course, object equality). Example sym-
bols are:
#aSymbol
#AndAnother
#+++
#very.long.symbol.with. periods
Identifiers
Identifiers in Little Smalltalk can be divided into three categories: instance
variables, class names, and pseudo-variables. An identifier beginning with
a capital letter is always a class name, whereas an identifier beginning
with a lowercase letter must represent either a pseudo variable or an
instance variable.
Syntax 15
At the command level, new instance variables can be defined merely
by assigning a value to a name. The assignment arrow is formed as a two-
character sequence consisting of a less than sign and a minus sign: 1
newname < - 17
Instance variables defined at the command level are known only at the
command level and cannot be used within a method for any class. As we
will see in a later chapter, instance variables within a class must all be
declared. .
Class identifiers respond to a variety of messages that can be used to
discover information concerning the class the object represents. For ex-
ample, the message respondTo, when passed to an object representing a
class, will cause the class to print a list of the messages to which instances
bf the class will respond.
Pseudo variables look like normal identifiers (that is, they are named
by a sequence of letters beginning with a lower case letter), but unlike
identifiers they need not be declared. There are several pseudo variables:
self, super, selfProcess
2
, true, false, nil, and smalltalk. Arguments for
a method (to be discussed shortly) are also considered to be. pseudo-var-
iables. Of the seven, self, super, and selfProcess are farthest from being
literal objects because their meaning depends entirely upon context. We
will discuss these in more detail when we describe class methods and
processes. The next three, true, false, and nil, are defined to be instances
(usually the only instances) of the classes True, False, and Unde-
finedObject, respectively. We will discuss these three in more detail when
we outline the behavior of different classes. The final pseudo variable,
smalltalk, is an instance of class Smalltalk and is used to centralize several
pieces of information concerning the currently executing environment.
Other types of objects in the Little Smalltalk system, such as blocks
and instances of user defined classes, will be discussed in later sections.
Messages
As noted in Chapter I, all actions in Smalltalk are produced by sending
messages to objects. This section begins by describing the syntax used to
produce messages.
1. From now on the text will use the symbol oE- to represent this two-character sequence.
2. The pseudo-variables selfprocess and smalltalk are unique to Little Smalltalk and are
not part of the Smalltalk-80 system, where different techniques are used to obtain the cur-
rently executing process or to obtain information about the current environment. See Ap-
pendix 5 for an overView of the differences between Little Smalltalk and the Smalltalk-80
programming environment.
16
"\
\. '.
>
.\
.'
The Language
Any message can be divided into three parts; a receiver, a message
selector, and zero or more arguments. The receiver and argument portions
of a message can be specified by other message expressions, or they may
be specified by a single token, such as an identifier or a literal.
The first type of message selector requires no arguments and is called
a unary message. A unary message selector consists of an identifier, the
first letter of which must be lowercase. For example:
7 sign
illustrates the message sign being passed to the number 7. Unary messages,
like all messages, elicit a response, which is simply another object. The
response to sign is an integer, either -1,0, or 1, depending upon the sign
of the object the message was sent to (the receiver). Unary messages parse
left to right, so, for example:
7 factorial sqrt
returns V7f, or approximately 70.993.
The second form of message, called a binary message, takes one ar-
gument. A binary message is formed from one or two adjacent nonalpha-
betic characters.
3
Binary messages tend to be used for arithmetic
operations, although this is not enforced by the system and there are
notable exceptions. An example of a binary message is arithmetic addition:
7 + 4
At first the fact that this is interpreted as "send the message + with
argument 4 to-the object 7"may seem strange; however, soon the uniform
treatment of objects and message passing in Smalltalk makes this seem
natural.
Binary messages, like unary messages, parse left to right. Thus
7 + 4 * 3
results in 33, not 19. Unary messages have a higher precedence than binary
messages, thus
7 + 17 sqrt
evaluates as 7 + (l7sqrt), not (7 + 17) sqrt.
The most general type of message is a keyword message. The selector
for a keyword message consists of one or more keywords. Each keyword
is followed by an argument. A keyword is simply an identifier (again, the
first character must be lower case) followed by a colon. The argument can
be any expression, although if the expression is formed using a keyword
3. Some characters, such as braces, parenthesis or periods, cannot be used to form
binary messages. See the description in Appendix 2 for a more complete description of the
restrictions.
\
~ ' - . . . , . -
Syntax 17
message, it must be placed in parentheses to avoid ambiguity. Example
keyword expressions are:
7 max: 14.
7 between: 2 and: 24
When we wish to express the name of the message being requested by
a keyword message, we catenate the keyword tokens. Thus we say the
message selector being expressed in the second example above is be-
tween:and:. There can be any number of keywords in a keyword message,
although in practice few messages have more than three.
Keyword messages have lower precedence than either binary or unary
messages. Thus
7 between: 2 sqrt and: 4 + 2
is the same as
7 between: (2 sqrt) and: (4 + 2)
Getting Started
You nowhave enough information to try getting some hands-on experience
using the Little Smalltalk system. After logging on, type the command st.
After a moment, the message "Little Smalltalk" should appear, and the
cursor should be indented by a small amount on the next line. If, at this
point, you type in a Smalltalk expression and hit the return key, the expres-
sion will be evaluated and the result printed. Try typing "3 + 4" and see
what happens. The result should be a 7, produced at the left margin. The
cursor then should advance to the next line and once more tab over several
spaces. Try typing "5 + 4 sqrt." Can you explain the outcome? Try "(5 +
4) sqrt."
Try typing
i <- 3
Notice that, since assignment expressions do not have a value, no value
was printed. However, if you now type
the most recent object assigned to the name will be produced.
The name last always contains the value of the last expression com-
puted. Try typing
27 + 3 sqrt
followed by
last
\.
.'\
\.
':-
.\
'>
,
.'
18 The Language
Finding Out About Objects
There are various messages that can be used to discover facts about an
object. The message class, for example, will tell you the class of an object.
Try typing
7 class
The message superClass, when passed to an instance of Class, will return
the immediate superclass of that class. Try typing
Integer superClass
7 class superClass
What is the superclass of Object?
The keyword message respondsTo: can be used to discover if an object
will respond to a particular message. The argument must be a symbol,
representing the message. Try typing
7respondsTo: #+
$A respondsTo: #between:and:
$A respondsTo: #sqrt
When passed to a ciass, the message respondTo: inquires whether instances
of the class respond to the given message. For example,
Integer respondsTo: #+
You can discover if two objects are the same using the binary message
= =. The the logical inverse of = =. Try typing
i 17
i == 17

One way to tell if an object is an instance of a particular class is to
connect the unary message class and the binary message = =. Try typing
i class ==Integer
A simple abbreviation for this is the message isMemberOf:. For ex-
ample, the last expression given is equivalent to
i isMemberOf: Integer
Suppose we want to tell if an object is a Number, but we don't care if
it is any particular kind of number (Integer or Float). We could use the
boolean OR bar (I), which is recognized by the boolean values true and
false:
(i isMemberOf: Integer)l(i isMemberOf: Float)
\
.\.
Syntax 19
\
.\.
-
-
Blocks
A simplier method is to use the message isKindOf;. This message asks
whether the class of the object, or any of superclasses, is the same as the
argument. Try typing
i isKindOf: Number
An interesting feature of Smalltalk is the ability to encapsulate a sequence
of actions and then to perform those actions at a later time, perhaps even
in a different context. This feature is called a block (an instance of class
Block) and is formed by surrounding a sequence of Smalltalk statements
with square braces, as in:
[ i ~ i +1. i print]
Within a block (and, as we will see in the next chapter, in general
within a method) a period is used as a statement separator. Since a block
is an object, it can be assigned to an identifier or passed as an argument
with a message or used in any other manner in which objects may be used.
In response to the unary message value, a block will execute in the context
in which it was defined, regardless of whether this is the current context
or not. That is, when the block given above is evaluated, the identifier i
will refer to the binding of the identifier i that was known at the time the
block was defined. Even if the block is passed as an argument into a class
in which there is a different instance variable i and then evaluated, the i
in the block will refer to the i in the context in which the block was defined.
Thus a block when used as a parameter is similar to the Algol-60 call-by-
name notion of a thunk.
The value returned by a block is the value of the last expression inside
that block. Frequently a block will contain a single expression, and the
value resulting from that block will be the value of the expression.
One way to think about blocks is as a type of in-line procedure dec-
laration. Like procedures, a block can also take a number of arguments.
Parameters are denoted by colon-variables at the beginning of the block,
followed by a vertical bar and then the statements composing the block.
For example,
[:x :y I (x + y) print]
is known as a two-parameter block (sometimes two-argument block). The
message value: is used to evaluate a block with parameters the number of
value: keywords given matches the number of arguments in the block. So,
for the example given above, the evaluating message would be value:value:.
20
\
.\.
\
~ \ - .
The Language
Comments and Continuations
A pair of double quote marks (II) are used to enclose a comment. One
must be careful not to confuse the double quote mark with two adjacent
single quote marks CI), which look very similar. The text of the comment
can be arbitrary and is ignored by the Little SmaIItalk system.
The Little Smalltalk system assumes that each line typed at the terminal
is a complete SmaIItalk expression. Should it be necessary to continue a
long expression on two or more lines, a special ineJication must be given
to the Little SmaIItaik system to prevent it from misinterpreting the partial
expression on the first line and generating an unintentional error message.
This special indication is a backwards slash C""-) as the last character on
all intermediate lines, for example:
2+ ""-
3*7 ""-
+5
40
EXERCISES
1. Show the order of evaluation for the subexpressions in the following
expression:
7/2 between: 7 + 17 sqrt and: 3 * 5
2. Type the following expressions:
7 = = 7
label = = label
#abe = = #abc
How do you explain this behavior?
3. What values will be printed in place of the question marks in the
following sequence:
i <- 17
j<-[i< +1]
j print
??
j print
??
i < - 23
i print
Program Continued
\.
Syntax 21
??
j value print
??
i print
??
j value print
??
i print
??
-
-
-
/\
'#: >i-
/\..
"'it'::
*

*
j',
*
j\

!\
*
'#:
:<.
!\
*
*
.\
*
*
/\
i}
* , .
*
*
*
*
, .
* /';,
*
*
.. i}
*
*
*
*
*
*

*

*
*
*
* <-: <:. <:.
///////////
'""'-" '"'"--"- '""-" "" "-
{{{{i{{{{{ {{{{{{{{{{{{{{{,
.J}}}}}}}}}}}}}}}}}}}}}}}}
(C««({««(((({{(,
/\
-: ...........

!\

!\

!\
!\

/\
/\


1\
(8':
,.
j\

!\

!\
/\

!\ :s:::
/;
:8\

///////,////
"'- '-,".
{{{{{{{{{{{{ {{{{{{{{{{{
f}}}}}}}}}}}}}}}}}}}n}}}}
:«««{({«{«««««i
))))))))))))))\\\\\\\\\
CHAPTER
3
Basic Classes
22
Basic Classes
,
.\.
23
\
.~ .
The classes included in the Little Smalltalk system can be roughly divided
into four overlapping groups. These groups are Basic Objects (Object,
UndefinedObject, Symbol, Boolean, True, False, Magnitude, Number,
Char, Integer, Float, Radian, Point), Collections (Collection, Bag, Set,
SequenceableCollection, KeyedCollection, Dictionary, Interval, List,
ArrayedCollection, Array, String, File, Random, ByteArray), Control
Structures (Boolean, True, False, Interval, Block), and System Man-
agement (Object, Class, Smalltalk, Process). The following sections will
briefly describe each of these categories. Appendix 3 provides detailed de;.
scriptions for each of the standard classes.
Basic Objects
The class Object is a superclass of all classes in the system and is used to
provide a consistent basic functionality and default behavior. For example,
the message = = is defined in class Object and is thus accepted by all
objects in the Little Smalltalk system. This message tests to see if the
expressions for the receiver and the argument represent the same object.
Another message defined in class Object is the message class, which re-
turns the object representing the class of the receiver.
The last chapter introduced the classes associated with literal objects.
other types of objects are also basic to many applications. For example,
instances of the class Radian are used to represent radians. A radian is a
unit of measurement, independent of other numbers. Only radians will
respond to trigonometric functions such as sin and cos. Numbers can be
converted into radians by passing them the message radians. Similarly,
radians can be converted into numbers by sending them the message
asFloat. Only a limited range of arithmetic operations on Radians, such
as scaling by a numeric quantity or taking the difference of two radians,
are permitted. Radians are normalized by adding or subtracting multiples
of 2'11" from their value.
The class Point is used to represent ordered pairs of quantities. Or-
dered pairs are useful in the solution of many problems, such as storing
coordinate pairs in graphics applications. In fact, the class Number pro-
vides a convenient method for constructing points. All instances of class
Number will respond to the message @ by producing a point consisting
of the receiver and the argument. Thus 10 @ 12 generates a point repre-
senting the ordered pair (l0,12). The first value is known as the x-value
and will be returned in response to the message x. The second value is the
y-value and is returned in response to the message y.
The class String provides messages useful in manipulating arrays of
characters. One important property of this class is that its instances are
the only objects in the Little Smalltalk system that can be displayed on an
24 The Language
output device such as a terminal or printer. Any object to be displayed
must first be converted into an instance of class String. The behavior
defined in class Object for the message print is to convert the object into
a string (using the message printString) and then to print that string (by
passing the message print to it).
The message printString is uniformly interpreted throughout the Little
Smalltalk systemas "produce a string representation ofyour value." Classes
for which this makes sense (such as Integer) must define a method for
this message that will produce the appropriate string. By default (that is,
by a method in class Object that will be invoked unless overridden), a
string containing the name of the class of the object is produced. In sub-
sequent chapters we will see several examples of how different classes
respond to the message printString.
Collections
The different subclasses and varieties of Collection provide the means for
managing and manipulating groups of objects in Smalltalk. The different
forms of collections are distinguished by several characteristics, whether
the size of the collection is &ed or unbounded, the presence or absence
of an ordering, and their insertion or access method. For example, an array
is a collection with a fixed size and ordering, indexed by integer keys. A
dictionary, on the other hand, has no fixed size or ordering and can be
indexed by arbitrary elements. Nevertheless, arrays and dictionaries share
many features, such as their access method (at: and at:put:) and their ability
to respond to collect:, select:, and many other messages.
The table below lists some of the characteristics of several forms of
collections.
Collections of one type can frequently be converted into collections of
different type by sending an appropriate message, for example, asBag (to
convert a collection into a Bag), asSet or asArray.
We can group the operations into several categories, independent of
the type of collection involved. The first basic action is adding an element
to a collection. Here collections divide into two groups. Those collections
that are indexed (Dictionary, Array) must be given an explicit key and
value, and, thus, the insertion method is the two-argument message at.·put:.
Those collections that are not indexed store only the value and thus use
the one argument message add:. A special case of this is the class List,
which maintains elements in a linear ordering. Here, values can be added
to the beginning or end of the collection by using the messages addFirst:
and addLast:.
Protocols for adding an element to a collection are similar to those for
removing an element from a collection. In collections that do not require
;,
\
'.
\
,
s
- Basic Classes 25
Name Creation Size Ordered? Insertion Access Removal
Method Fixed? Method Method Method
Bag/Set new no no add: includes: remove:
Dictionary new no no at:put: at: removeKey:
Interval nto: m yes yes none at: none
List new no yes addFirst: first removeFirst
addLast: last removeLast
Array new: yes yes at:put: at: none
String new: yes yes at:put: at: none
a key, an element can be removed with the message remove:, the argument
being the object to be removed. In keyed collections, the removal message
uses the key (removeKey:), and not the value. In collections with fixed sizes
(Array and String), elements cannot be removed. In a List, an element
can be removed from either the beginning of the list (removeFirst) or the
end of the list (removeLast).
Once an element has been placed into a collection, the next step is to
access the element. Those collections using keys require a key for access
and use the message at:. For those that do not require a key, the only
question (since one already has the value)-is whether the value is in the
collection. Thus the appropriate message is includes: (which also works
for keyed collections). A special case is List where one can access either
the beginning or the end of the list by using the messages first and last.
The access methods at: and includes: access a value by position. Fre-
quently, however, one needs to access an element by value without knowing
a position. For example, one may want to find the first positive element
in an array of integers. To facilitate this search there is a message named
detect:. The message detect: takes as an argument a one-parameter block.
It evaluates the block on each element in the collection and returns
the value of the first element for which the block evaluates true. For ex-
ample, if x is an array containing numeric values, the message detect: could
be used to discover the first positive value.
x ~ # ( - 2 - 3 4 5)
x detect: [ :y Iy > 0 ]
4
An error message is produced and nil returned if no value satisfies the
condition. This can be changed using the message detect:ifAbsent:
\
\ \
\
: ~
i-
26 The Language
x detect: [ :y I y > 10 ]
error: no element satisfies condition
nil
x detect: [ :y I y > 10] ifAbsent: [ 23 ]
23
In ordered collections, the search is performed in order, whereas in
unordered collections, the search is implementation dependent, and no
specific order is guaranteed.
If, instead of finding the first element that satisfies some condition,
you want to find all elements of a collection that satisfy some condition,
then the appropriate message is select:. Like detect:, select: takes as an
argument a one-parameter block. What it returns is another collection, of
the same type as the receiver, containing those values for which the ar-
gument block evaluated true. A similar message, reject:, returns the com-
plementary set.
x select: [ :y I y > 0 ]
#( 45)
x reject: [ :y Iy > 0 ]
#( -2 -3)
The message do: can be used to perform some computation on every
element in a collection. Like select: and reject:, this message takes a one-
argument block. The action performed is to evaluate the block on each
element of the collection.
x do: [ :y I (y + 1 ) print]
-1
-2
5
6
The message do: returns nil as its result. If, instead of performing a
computation on each element, you want to produce a new collection con-
taining the results, the message collect: can be used. Again like select: and
reject:, this message takes as argument a one-parameter block and returns
a collection of the same variety as the receiver. The elements of the new
collection, however, are the results of the argument block on each element
of the receiver collection.
x collect: [ :y I y sign]
#( -1 -1 1 1 )
Frequently the solution to a problem will involve processing all the
values of a collection and returning a single result. An example would be
taking the sum of the elements in a numerical array. In Little Smalltalk,
the message used to accomplish this is inject:into: The message inject:into:
takes two arguments: a value and a two-parameter block. The action per-
formed in response to this message is to loop over each element in the
Basic Classes 27
collection, passing the element and either the initial value or the result of
the last iteration as arguments to the block. For example, the sum of the
array x could be produced using inject:
x inject: 0 into: [ :a :b I a + b ]
4
The following command returns the number of times the value 4 occurs
in x:
x inject: 0 into: [ :a :b I (a = =4) ifTrue: [ b + 1 ] ifFalse: [ b ]]
1
We have described the broad categories of messages used by collec-
tions. There are many other messages specific to certain classes; they are
described in detail in Appendix 3. We next will provide a brief overview
of the most common types of collections.
The classes Bag and Set represent unordered groups of elements. An
element may appear any number of times in a bag but only once in a set.
Elements are added and removed by value.
A Dictionary is also an unordered collection of elements; however,
unlike a bag, insertions and removal of elements from a dictionary requires
an explicit key. Both the key and value portions of a dictionary entry can
be any object, although commonly the keys are instances of String, Sym-
bol or Number.
The class Interval represents a sequence of numbers in an arithmetic
progression, either ascending or descending. Instances of Interval are
created by numbers in response to the message to: or to:by:. In conjunction
with the message do:, an Interval creates a control structure similar to do
or for loops in Algol-like languages.
(1 to: 10 by: 2) do: [ :x I x print]
1
3
5
7
9
Although instances of class Interval can be considered to be a collection,
they cannot have additional elements added to them. They can, however,
be accessed randomly using the message at:.
(2 to: 7 by: 3) at: 2
5
A List is a group of objects having a specific linear ordering. Insertion
and removal is from either the beginning or the end of the collection. Thus
a list can be used to implement both a stack and queue.
A File is a type of collection in which the elements of the collection
are stored on an external medium, typically a disk. A file can be opened
28 The Language
in one of three modes. In character mode every access or read returns a
single character from the file. In Integer mode every read returns a single
word as an integer value. In string mode every read returns a single line
as an instance of class String. Elements cannot be removed from a file,
although they may be overwritten. Because access to external devices is
typically slower than access to memory, many of the operations on files
may be quite slow.
An Array is perhaps the most commonly used data structure in Little
Smalltalk programs. Arrays have fixed sizes, and, while elements cannot
be inserted or removed from an array, the elements can be overwritten.
Literal arrays can be represented by a pound sign preceding a list of array
elements, for example:
#( 2 $a 'joel 3.1415 )
A String can be considered to be a special form of array, where the
elements must be characters. In addition, as we have been illustrating in
many examples, a literal string can be written by surrounding the text with
quote marks.
The class ByteArray represents a special form of array where each
element must be a number in the range 0 through 255. Byte arrays are
used extensively in the internal representations of objects in the Little
Smalltalk system. Byte arrays can be written as a pound sign preceding a
list of elements enclosed in square braces, for example:
#[ 0 127 32 115 ]
There are two other classes that are commonly used to represent groups
of data, although they are not subclasses of Collection. The class Point,
already discussed, can be considered to be a small collection of two items.
The class Random can be thought of as providing protocol for an infinite
collection of pseudo-random numbers. This "list," of course, is never ac-
tually created in its entirety; rather each number is generated as required
in response to the message next. The values produced by instances of class
Random are floating values in the range 0.0 to 1.0. Other messages can be
used to convert this into either an integer or a floating value in any range.
Control Structures
One of the more surprising aspects of Smalltalk is the fact that control
structures are not provided as part of the basic syntax but rather are defined
using the message passing paradigm. The basic control structure in Small-
talk, as in most computer languages, is the conditional test: IF some con-
dition is satisfied THEN perform some actions ELSE perform some other
actions. In Smalltalk this is accomplished by passing messages to instances
Basic Classes
\
~ \ - .
29
of class Boolean. The class True (a subclass of Boolean) defines methods
for the messages ifTrue: and ifFalse: (similar methods are defined for class
False). The arguments used with these messages are blocks. If the con-
dition is satisfied (Le., the receiver is true and the message is ifTrue:, or
the receiver is false and the message is ifFalse:), the argument block is
evaluated, and the result it produces is returned. If the condition is not
satisfied, the value nil is returned.
(3 < 5) ifTrue: { 17 ]
17
(3 < 5) ifFalse: { 17 ]
nil
The combined forms ifTrue:ifFalse: and ifFalse:ifTrue: are also recog-
nized:
(3 < 5) ifTrue: { 17 ] ifFalse: { 23 ]
17
(3 > 5) ifTrue: { 17 ] ifFalse: { 23 ]
23
The message and: and or: are similar to ifTrue: and ifFalse:. They are
also used with booleans and passed as arguments objects.of class Block.
And: i and or: provide "short circuit" evaluation of booleans; that is, the
argument block is evaluated only if necessary to determine the result of
the boolean expression.
((i < 10) and: { (b at: i) =4] ifTrue: { i print]
In this example, the expression "(b at:i) =4" will be evaluated only if the
expression <10<10)" is true. If the first expression returns false, the argu-
ment block used with the and: message is not evaluated.' Notice that the
relational < returns either true (an instance of class True) or false (an
instance of class False) and that the message and: is implemented in class
Boolean, a superclass of both True and False. Various other boolean
operations, such as not are also defined in this class.
Next to conditional tests, the most common control structure is a loop.
A loop is produced by passing the timesRepeat: message with a parame-
terless bock as an argument to an integer. The value of the integer is the
number of times to execute the loop. For example;
5 timesRepeat:{ 8 print]
8
8
1. In actual fact the Little Smalltalk parser will, for efficiency, often optimize conditions
to remove the message passing overhead. Nevertheless, the underlying paradigm holds true
and will, in fact, be used under some conditions (for example, when the arguments are not
a block).
30
\
. ~ .
\
\
~ \
::", :."
The Language
8
8
8
will print the number 8 five times.
A more general loop is used to produce numbers in arithmetic pro-
gression. The messages to: and to:by:, when passed to a number, produce
an instance of class Interval. As we noted in the last section, an interval
is a collection of values in arithmetic progression. We can then use the
message do: to enumerate the elements in the progression. For example:
(2 to: 9 by: 3) do: [ :x I x print]
2
5
8
A more general form of loop is the while loop. A while loop is formed
using blocks as both receiver and argument. The result of the receiver
block must be a boolean. The actions performed by the block are to evaluate
the receiver block and, as long as it returns true, to evaluate the argument
block. For example, by using an additional variable the previous loop could
have been written:
i ~ 2 .
[ i < = 9 ] whileTrue: [ i print. i ~ i + 3 ]
2
5
8
Since both the receiver and the argument block can contain any number
of expressions (the value of a block is always the value of the last expression,
regardless of how many expressions the block contains), sometimes no
argument block is necessary. The unary message whileTrue (or while-
False) can then be used. For example:
i ~ 2 .
[i print. i ~ i + 3 . i < = 9] whileTrue.
2
5
8
Class Management
The class Class is used to provide protocol for manipulating classes. Thus,
for example, the methods new and new: are implemented in class Class
to allow instance creation. Classes themselves cannot be created by new
Basic Classes 31
but must be generated by compilation (which is the topic of the next
chapter).
The messages new and new: are treated differently in one respect by
the Little Smalltalk system: if a class defines a method for these messages,
then, each time a new instance of the class is created (by sending the
message new or new: to the class object), the newly created object is im-
mediately initialized by sending it the same message, and the resulting
object is returned to the user. This happens at all levels of the class hier-
archy, even if the message is defined multiple times. (That is, later
definitions of new are in addition to, and do not override, definitions higher
in the class hierarchy). One should be careful to distinguish the message
new passed to the class object used to create the object from the same
message passed to the newly created object used for initialization. Since
the second message is produced internally, and not by the user, it is easy
to overlook.
Array new: 3
#( nil nil nil )
UnoefinedObject new
nil
The argument used with new: is not used by the object creation protocol
but only by the object initialization method. In later chapters we will see
how this feature can be used to automatically initialize objects.
The class Smalltalk provides protocol for the pseudo variable small-
talk. By passing messages to smalltalk, the user ~ a n discover and set
many attributes of the currently executing environment.
smaIitaIk date
Fri May 24 14: 03: 16 1985
Another message, time:, requires a block as argument. The integer value
it returns represents the number of seconds elapsed in evaluating the block.
smalltalk time:[ ( 1 to: 10000 ) do: [:x I ] ]
104
Smalltalk is a subclass of Dictionary and thus responds to the mes-
sages at: and at:put:. Since smalltalk is accessible anywhere in any object,
it can be used to pass information from one object to another or to provide
global information used by a number of objects. Of course, it is the usees .
responsibility to insure that two objects do not try to store different in-
formation using the same key. With the exception of message passing, this
pseudo variable is the only means of communication between separate
objects. Although permitted, the use of the pseudo-variable in this manner
is at odds with the pure object-oriented philosophy of Little Smalltalk and
should be discouraged. The necessity for global variables is often the mark
of a poorly developed solution to a problem.
\ \
~ .
.,.
~ ~
~ : } >
32 The Language
The pseudo variable smalltalk also provides a means to construct and
evaluate messages at run time by using the message perfonn:with
Arguments:. The first argument to this message must be a symbol indicating
the message to be processed. The second argument must be an array
representing the receiver and arguments to be processed. The second ar-
gument must be an array representing the receiver and arguments to be
used in evaluating the message. The response is the value returned by the
first argument of this array in response to the message, with the remainder
of the arguments in the second array as the argument values for the mes-
sage. For example:
smalltalk perform: #between:and: withArguments: #(3 1.03.14)
True
An instance of class Process is used to represent a sequence of Small-
talk statements in execution. Processes cannot be created directly by
the user but are created by the system or by passing the message new-
Process or fork to a block. Processes will be discussed in more detail in
Chapter 10.
Abstract· Superclasses
We did not discuss the classes Collection, KeyedCollection,
SequenceableCollection, or ArrayedCollection in the last section, even
though they were listed as forms of "collection" in the beginning of this
chapter. These classes, along with such classes as Boolean, Magnitude,
or Number, are what are known as abstract superclasses. Instances of
abstract superclasses are seldom useful by themselves, but the classes are
important in providing methods that can be inherited by subclasses. For
example, while it is legal in Smalltalk to say:
x ~ Collection new
and the resulting variable x will indeed be an instance of class Collection,
the object is not particularly useful. It has no insertion or deletion protocol,
for example. An instance of class Set, however, is very useful, and the
messages defined in class Collection are important in providing function-
ality to objects of this class and to other subclasses of class Collection.
The selection and design of abstract superclasses is one of the more
important arts in Smalltalk programming. For example, if one were design-
ing a system to manipulate banking accounts, a class Account might be
a useful abstract superclass for classes CheckingAccount and
SavingsAccount. The actions specific to the individual types of accounts
would be in the subclasses, whereas any common behavior, such as the
actions necessary for opening and closing an account or querying the
balance, might be implemented in the superclass.
-
-
EXERCISES
Basic Classes 33
1. Suppose you created a new instance of the class UndefinedObject, as
follows:
new
How does i respond to print? To isNil or notNil? To the object com-
parison message = = with nil ? Is i nil? List facts to support your
answer.
2. Note that the messages arcSin and arcCos produce an object of type
Radian and not of type Float. Furthermore, only objects of type Radian
respond to sin and cos. An alternative would have been to eliminate
the class Radian and to permit all objects of class Float to respond
to the messages sin and cos. Discuss the advantages and disadvantages
of these two different arrangements.
3. Suppose you have a Bag containing numbers. Howwould you go about
producing an instance of the class List containing the numbers listed
in sorted order?
4. What is the class of Class? what is the superclasss of Class?
5. Many times, two or more different sequences of messages to collections
will have the same effect. In each of the following, describe a sequence
of messages that will have the same effect ,\S the indicated message.
a) implement reject: in terms of select:
b) implement size in terms of inject:into:
c) implement includes: in terms of inject:into:.
"\
.\. \-
/ / / /,//// / / ,/
"'" '""'" "'" '""'" "", '"""'-"
{{{{{{{{H{{{{{{{{{{{{{W
.}}}H}}}}}}}}}}}}}}}}}}}r
««{{{««««{«««(,
,/ / / ////////
"" '"'""'"'"'"'---- '"""
;{{{{ {{{{{ {{{{ {{{{{{{H{{{
}}}}}}}}}}}}}}}}}}}}}}}}}}
{«{«({{«««««««i
\\\)))))))))\\\\\\)))))
CHAPTER
4
Class Definition
34
'.
\
"\ \ ,
,
.' .\
Class Definition 35
The last chapter introduced some of the standard classes in the Little
Smalltalk system. This chapter will show how the user can define new
classes to provide additional functionality for the Smalltalk language.
Class descriptions cannot be entered directly at the keyboard during
a Little Smalltalk session. Instead, class descriptions must be prepared
beforehand in a file, using a standard editor. There can be any number of
class descriptions in a file, although it is convenient to keep each class
description in a separate file for ease in program development. The textual
representation of a class description consists of two main parts, the class
Heading, followed by a sequence of one or more m.ethod descriptions. We
will discuss each portion in turn. The syntax for class descriptions is given
in detail in Appendix 2.
Figure 4.1 shows a prototypical class description, in this case the de-
scription for the class Set. The initial part of the description, the class
header, consists of the keyword Class (the first letter capitalized) followed
by a class name (also capitalized). Following the class name, the superclass
name preceded by a colon, is given. The superclass name is optional and,
if not given, the class Object will be assumed.
After the class and superclass names, the class description lists instance
variable names. Instaqce variables provide the local memory for instances
of the class. Each class instance is given its own set of instance variables,
and no other object can modify or change an instance variable in another
object. The list of instance variable names is surrounded by a pair of
vertical bars. Note that Smalltalk has no notion of variables being declared
to be of a specific type, thus any instance variable can contain any accessible
object or expression. Although the syntax is free form, it is conventional
to place the instance variables on a line separate from the class name.
Instance variables are initially given the value nil. Ifa class does not contain
any instance variables, the entire list, including the vertical bars, can be
omitted. Following the heading, a pair of square braces surround the meth-
ods that comprise the protocol for the class.
Each method in the class protocol defines how instances of the class
Figure 4.1 0 Class description for class Set
Class Set: Collection
I list I
[
first message pattern
first message method
second message pattern
second message method
36
\
The Language
will respond. to a single message. The particular message being defined by
the method is given by the message pattern. The pattern is the first of the
three major portions ofa method; the other two portions are a list of
temporary variables and the message body.
A message pattern defines both the name of the message for which the
method is providing protocol and the names to be used when referring to
parameters associated with the message. There are three forms of message
pattern, corresponding to the three forms of messages (unary, binary, and
keyword). Example methods using two of those forms are given in Figure
4.2. These methods are from class Collection. Note that method descrip-
tions are separated by vertical bars. The one exception to the free form
notation for class descriptions is that this vertical bar must appear in the
first column.
An optional list of temporary identifier names can appear following a
message pattern. For example, the message description for the message
do: in Figure 4.2 lists a temporary identifier named item. Temporary iden-
tifiers are created when the method is initiated in response to a message,
and they exist as long as the message body is being executed or there is a
block in existence that can refer to the temporary value. Like instance
variables, temporary variables are initially given the value nil.
Amethod body consists of a sequence of one or more Smalltalk expres-
sions separated by periods. Note that the period is an expression separator,
not an expression terminator, and does not follow the final expression in
the method body. The final expression in a method body, or the final
expression in any block created within the method body, can be preceded
by an up arrow to indicate that the following expression is to be returned
as the response to the message. (On some terminals the up arrow looks
like a <lcaret" or".) If no explicit value is returned, the default action at the
end of a method is to return the receiver as the response to the message.
Figure 4.2 0 Method descriptions from class Collection
do: aBlock I item I
item oE- self first.
[ item notNil ] whileTrue:
[ aBlock value: item. item oE- self next ].
t nil
isEmpty
t (self size = 0)
remove: oldObject
self remove: oldObject ifAbsent:
[t self error: 'attempt to remove object not found in collection' ].
t oldObject
Class Definition 37
\.
Note that the up arrow indicates an immediate return from the current
method description even if the arrow occurs within a block. This is different
from a block returning a value, which, as we saw in Chapter 2, is implicitly
the value of the last expression in the block.
Within a method body there are four types of variables that can be
accessed, namely instance variables, argument variables, temporary var-
iables, and pseudo variables. In addition to the pseudo variables discussed
in the last chapter (true, false, nil and smalltalk), the pseudo variables
self, super, and selfProcess can be used. Both the variables self and super
refer to the receiver of the current message. When a message is passed to
self, the search for a method begins with the class description associated
with the class of the receiver. On the other hand, when a message is passed
to super, the search begins with the class description of the superclass of
the class in which the current message is defined. For example, if an
expression in class Set (Figure 4.1) passed a message to super, the search
for a matching method would begin in class Collection, the superclass of
class Set. Amessage passed to self would initiate a search for an associated
method in class Set.
To illustrate the actions of self, suppose variable "x" is an instance of
class Set. The methods shown in Figure 4.2 are from class Collection, a
superclass of class Set. In response to the message "x isEnzpty, " the method
isEmpty shown in Figure 4.2 is initiated. This method, in turn, passes the
message size to the pseudo variable self, which in this case represents x.
Thus the search for a nlethod matching the message size would begin in
class Set, the class of x. If, on the other hand, the method for isEmpty had
passed the message size to the pseudo variable super, the search for a
corresponding method would have begun in the class Object, the super
class of Collection.
Two messages are singled out for special treatment. If either of the
messages new or new: is defined in a class protocol, then, when an instance
of that class is created, the associated message (either new or new:) will
automatically be passed to the new instance before any further processing.
Thus, these messages can be used to provide automatic initialization of
instance variables. The next section describes in detail one class, the class
Set, and illustrates the use of this feature.
An Illustrative Example
Figure 4.3 gives the complete class description of the class Set. A set is a
collection of objects; each object occurs no more than once in the collec-
tion. (See Exercise 2.) The data structure used to implement the set will
be a list. Recall that a list is also a collection but one that maintains values
in order.
38
Figure 4.3 D Class description for Set
Class Set :Collection
I list I
[
new
l i s t ~ L i s t new
The Language
add: newElement
(list includes: newElement)
ifFalse: [list add: newElementJ
remove: oldElement ifAbsent: exception Block
list remove: oldElement ifAbsent: exceptionBlock
size
t list size
occurrencesOf: anElement
t (list includes: anElement) ifTrue: [ 1 J ifFalse: [ 0 J
first
t list first
next
i list next
As we have already seen, the class Set maintains one instance variable,
list. Each instance of class Set will have a separate instance variable that
cannot be accessed by any other object. The method for message new will
automatically create an instance of class List in the variable list whenever
an instance of class Set is created.
When an element is added to a set, a test is first performed to see if
the element is already in the set. If so, no further processing is done; if
not, the element is added to the underlying Jist. Similarly, to remove an
element, the actions in the method in Set merely pass the removal message
on to the underlying list. Note that the long form of the remove: message
is defined; it includes a second argument, indicating what actions should
be taken if the given item is not found. As we saw in Figure 4.2, the class
Collection defines the short message (re1110ve:) in terms of this longer
message. Thus either form ofthe message can be used on a Set.
The messages first and next are used to produce generators. Generators
will be the topic of a later chapter; it is sufficient to say here that first and
next provide a means to enumerate all elements in a collection. The mes-

\
\ \
~ ~ -
,
~ \
.\:
Class Definition 39
sage first can be interpreted informally as flinitialize the enumeration of
items in the collection and return the first item, or nil if there are no items
in the collection." Similarly, next can be defined as "return the next item
in the collection, or nil if there are no remaining items." The protocol for
do: (Figure 4.2) illustrates how these messages can be used to construct a
loop to access every item in a collection. In Class Set, the generation of
items in response to these messages is provided by passing the same mes-
sages to the underlying list.
Processing a Class Definition
Once you have created a class description, the next step is to add the class
to the collection of standard classes provided by the Little Smalltalk system.
Suppose, for example, you have defined a class Test in the file test.st; to
add this class definition to a running Little Smalltalk execution you would
type the following command in place of a Smalltalk expression:
)i test.st
The i can be thought of as mnemonic for "include." At this point the
class description will be parsed, and, if there are syntax errors, a number
of descriptive error messages will be produced. Ifthere are no syntax errors,
the class definition will be added to the collection of classes known to the
Little Smalltalk system. Whether there are syntax errors or not, the cursor
should advance by one tab stop when the system is ready to accept the
next command.
If there are syntax errors, the class description can be edited without
leaving the Little Smalltalk system. To do this, type the following com-
mand:
)e test.st
The )e command will place the user in an editor mode to view the
designated file.' When the user exits the editor, another attempt will be
made to include the file, as if the )i command were typed.
Once a class definition has been successfully included, the defined class
can be used the same as any other class in the Little SmalltaIk system. For
example, a new instance can be created using the command new.
i ~ T e 5 t new
There are other system commands, similar to )i and k, described in
1. On UNIX Systems the editor selected can be changed by modifying the EDITOR
environment variable.
40
-
-
EXERCISES
The Language
Appendix 1. These commands are provided with an alternative syntax,
rather than as messages passed to smalltalk, because they are used during
the bootstrapping process before any objects have been defined. The boot-
strapping of the Little Smalltalk system will be discussed in the second
section of this book.
1. A Bag is similar to a Set; however, each entry may occur any number
of times. One way to implement the class Bag would be to use a
dictionary, similar to the way a List was used to implement the class
Set in Figure 4.3. The value contained in the dictionary for a given
entry would represent the number of times the entry occurs in the bag.
The framework for such an implementation is shown below. Change
the name of the class from Bag to MyBag, and complete the imple-
mentation of class Bag.
Class Bag :Colleetion
I diet count I
[
new
new
several missing methods
first
(count diet first) isNil ifTrue: [ t nil].
count count - 1.
t diet currentKey
next
[count notNil]whileTrue:
[(eount>O)
t diet currentKey]
ifFaIse: [eou iet next]].
t nil
Keep in mind that instances of Dictionary respond to first and next
with values and not keys. However, the current key is accessible if you
use the message currentKey.
2. The collections described in Chapter 3 were all linear, meaning they
could all be represented by a linearly written list. Acommon nonlinear
data structure in computer science is the binary tree. Implement the
Class Definition 41
class BinaryTree. Instances of BinaryTree contain a left subtree (or
nil), a right subtree (or nil), and a value. Instances of the class should
respond to the following messages:
left Return the left subtree, usually either nil or other instances
of BinaryTree.
left: Set the left subtree to the argument value.
right Return the right subtree.
right: Set the right subtree to the argument value.
value Return the value of the current node.
value: Set the value of the current node to the argument.
How should instances of BinaryTree respond to the enumeration mes-
sages first and next? How about print or printString? What should be
.the superclass of BinaryTree?
1\
/\
/\
A
f\
f\
.,
f\
/\
.i\
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
: '
<.:: .<: < <..:. < <
\
-
.///// / / / /_.//
""""". """"""'.'-"
{{H{{{{{{H{{{{{{ {{ {{{{{1
.}}}}}}}}}}}}}}}}}}}}}}}}},
«««««««««««(,
/\
*
...
!\
(8)

*
*
/\
:it:
* i\

%
*
f\

%
*
" f ..
x- f\
'%: 1.
(§::
*
f\

* f\
...
*
*
/\
r--.
*
. :"""'\,
%
*
1\

:::j;:
* f\

*
(2E
*
*
. ,
($)
* f\

*
1.
;S0
'%:
*
/\
*
*
/\


*
%
*
"
<: <"...::. <::: <;: <::.. <: ;«::..
<::
/ ////////'//
""-, """""" "" """""".
'I' "{'{{I' '{{{{f 'J{ f{{1 ("
)U _"dl t )Ht
f}}}}}}}}}}}}}}}}}}}}}}}}}
((({(((((((((1
\\)))l)))))))))))))))))
CHAPTER
5
A Simple Application
42
"\
.\.
A Simple Application 43
This chapter depicts the development of a simple Smalltalk application.
The application chosen, a tool to help keep track of employee information
in a small business, is considerably less important than the design tech-
niques being illustrated.
The first, and probably one of the most important steps, is deciding
exactly what you want to do. This involves not only stating the desired
functionality, but also describing the intended user interface (the set of
messages to which the application should respond).
Because programming in Smalitalk is so easy compared to many other
programming languages, a particularly attractive technique of developing
an application is rapid-prototyping. Using this technique, you first define
a minimal system that will still exhibit the important aspects of the desired
functionality. That is, you strive to find the core of the functions you want
at the expense of enhancements or features you might think ultimately
desirable. You then design the simplest, most straightforward implemen-
tation of this bare system, ignoring for the moment such features as user
friendliness and efficiency.
Once you have an executable system, you experiment with it. There is
a great psychological benefit to having an executing system, even one that
is very simple. High level logical mistakes are most easily exposed with
the aid of an executing system, and thus a working version helps consid-
erably in getting more complex versions mnning. An important aspect of
software is its feel, a notion nearly impossible to define and almost as
difficult to predict before you have a working system.
The n ~ c e s s i t y for devoting time to a complete and thorough job of
specification and design cannot be understated. However, it is likely that
there will be many changes in the design stage of any realistic piece of
softwar:e. It js also likely (perhaps unfortunately) that the user's concept
of the problem at hand and the correct solution also will change as the
user's experience with the initial versions increases. Users often discover
that the set of enhancements they thought were important before a project
was implemented are not the same as those they want after experimenting
with the initial version. Thus time spent creating a complete system, be-
yond a minimal system for experimentation purposes may not be produc-
tive. (Even with the best planning it may be necessary to throwaway at
least one version, and oftentimes more, and start anew. This is not a sign
of poor programming practice. It is far better to throwaway the first
attempt at a program, after learning from the mistakes, and to rewrite it
than to take a poorly designed and less than adequate program and try to .
fix it by "patching.")
What is the minimal functionality we could desire for our employee
database? At the simplest, we must be able to add and delete information
from the records. But what information? Let us start with four fields:
name
idNumber
a string giving the name of the employee
a unique internal identification number
1 ~
\
\
\
>
"
44 The Language
position a symbol representing the employee job classification
salary a number giving the salary of the employee
In a realistic situation there would probably be many more fields one
would want to maintain (length of employment, social security number,
department, and so on) but the four fields listed are sufficient for our
examples. For a first approximation, we need not create any new classes
at all. We can merely use an instance of class Dictionary as our database
and an Array for each employee. The key for each dictionary entry can be
the employee number (since they must be distinct, and names may not
be) and the value field can be the rest of the information.
employeeDatabase ~ Dictionary new
employeeDatabase at: 14737 put: # ( 'David Smith
'
# clerk 14000 )
employeeDatabase at: 16432 put: # ( 'Roger Jones
l
# clerk 13500)
employeeDatabase at: 2431 put: #( 'Fred Brown
'
# president 68020 )
Information on a particular employee can be extracted using at:
employeeDatabase at: 16432
# ( lRoger Jones
l
# clerk 13500 )
Searches of various sorts can be performed by using select:
employeeDatabase select: [:x I (x at: 2) = = # president]
Dictionary ( 2431 @ #( 'Fred Brown
'
president 68020 ) )
employeeDatabase select: [:x I (x at: 3) < 20000 ]
Dictionary ( 14737 @ #( 'David Smith
l
#c1erk 14000 )
16432 @ # ( 'Roger Jones
l
# clerk 13500 ) )
Output which looks slightly better can be obtained by using do:
employeeDatabase do: [:x I «x at: 3) < 20000) ifTrue: [ x print] ]
#( 'David Smith
'
#c1erk 14000)
#( 'Roger Jones
l
#c1erk 13500)
A record can be updated by combinations of at: and at:put:
(employeeDatabase at: 16432) at: 3 put: 13750
employeeDatabase at: 16432
# ( 'Roger Jones
l
# clerk 13750)
While it required almost no work to produce this first approximation,
it is obvious that this scheme has some deficiences. One of the most serious
deficiencies is that the user of the database must know the position of each
field in the employee record in order to understand or update the infor-
mation. One mistake in updating (using the wrong field number, for ex-
ample) can damage the record badly. Therefore our first improvement will
be to replace each employee record with an i n s t a n c ~ of a new class, Em-
ployeeRecord. Instances of EmployeeRecord will themselves contain
instance variables for each field of interest. A pair of messages is defined
\
\
~ } -
A Simple Application 45
for each field: one to set the value and one to return it. The following class
description shows one of these pairs.
Class EmployeeRecord
I name idNumber position salary I
[
name: aString
name ~ aString
name
t name
We still use a Dictionary for the entire database but replace the entries
in the dictionary by instances of EmployeeRecord. In a certain sense we
have complicated matters since it is now necessary to initialize each field
separately. Also it is now possible to retrieve only a single field, not the
entire structure, of the employee record.
employeeDatabase ~ Dictionary new
employeeDatabase at: 2431 put: EmployeeDatabase new
(employeeDatabase at: 2431) name: IFred Brown
'
(employeeDatabase at: 2431) position: #president
(employeeDatabase at: 2431) salary: 68020
employeeDatabase at: 2431
EmployeeRecord
(employeeDatabase at: 2431) position
#president
Let us deal first with the problem of the difficulty in retrieving all
information at once. The message printString is used uniformly throughout
the Little Sn1alltalk system to produce a printable representation of an
object. If no method is provided for this message, the default method (in
class Object) produces the class name, as illustrated above. We can pro-
duce a more meaningful output, however, by concatenating the various
fields with strings showing the field names.
printString
t Iname: II name,
I position: I, position,
Isalary: I I salary
Now when we try to print an individual record, the result is more helpful.
employeeDatabase at: 2431
name: Fred Brown position: president salary: 68020
The solution to the problem of initialization is slightly more compli-
cated. As a first step, we can create a new message initialize and use the
46
\
\
\
\
-,.
"
~ \
The Language
message getString with the pseudo variable smalltalk to return a string in
response to a prompt. In order for the prompt and the response to appear
on the same line, we use the printing command printNoReturn.
initialize
I name: I printNoReturn.
name <E-- smalltalk getString.
'position: I printNoReturn.
position <E-- smalltalk getString asSymbol.
'salary: I printNoReturn
salary <E-- smalltalk getString aslnteger
Thus we can initialize all the fields at the same time with one command:
employeeDatabase <E-- Dictionary new
employeeDatabase at: 2431 put: EmployeeRecord new
(employeeDatabase at: 2431) initialize
name: Fred Brown
position: president
salary: 68020
employeeDatabase at: 2431
name: Fred Brown position: president salary: 68020
There is a shortcut in Smalltalk to make the initialization of newly
created objects easier. If the class of an object defines a method for the
message new, then this message is passed to each new instance of that
class as it is created, before the new object is returned to the user. Thus,
we can define the following message in class EmployeeRecord:
new
self initialize
The message initialize will then be sent automatically to each new instance
of EmployeeRecord.
employeeDatabase <E-- Dictionary new
employeeDatabase at: 2431 put: EmployeeRecord new
name: Fred Brown
position: president
salary: 68020
employeeDatabase at: 2431
name: Fred Brown position: president salary: 68020
One can update a record in the same manner as initialization. That is,
to update a record on a specific individual, you merely pass the message
initialize to the record for that-individual and retype the information, mak-
ing changes where appropriate. We still can use the messages we defined
at the start to update single fields individually. Both of these alternatives
may be error prone. A better scheme would be to display each field as it
'.
.\
A Simple Application 47
is currently contained in the database, then making any changes desired.
As with initialize, the systemwould prompt for each field, giving the current
value of the field. If the user merely typed return, the current value would·
be retained.
(employeeDatabase at: 2431) upDate
name (Fred Brown):
position (president):
salary (68020): 72000
employeeDatabase at: 2431
name: Fred Brown position: president salary: 72000
Here only the salary field was changed.
Note that we are performing the same actions for each field. Thus it
is easier to abstract the desired behavior into a lower level message, passing
messages to self for each individual field. For that lower level message, it
is necessary to print the prompt, including the current value; retrieve the
user's response, and, if blank, return the current value, or, if not blank,
return the user's response. Thus there are two essential pieces of infor-
mation that must be passed as arguments; namely, the string with which
to prompt and the current value for the field. We can write upDate as
follows:
upDate
name ~ self promptString: Inamel currentValue: name
position ~ (self promptString: 'position' currentValue: position) asSymbol
salary ~ (self promptString: 'salari currentValue: salary) aslnteger
I
promptString: aString currentValue: aValue I reply I
( promptString, 1( I, currentValue, I ):1) printNoReturn.
reply ~ smalltalk getString.
(reply size = 0)
ifTrue: [ i currentValue]
ifFalse: [ t reply]
Now let us return once more to the representation of the database as
a whole. We have up to this point merely been using an instance of class
Dictionary and the insertion and deletion messages at: and at.·put:. Thus
the abstract actions, namely creating, updating, and listing employee en-
tries, must be phrased (sometimes awkwardly) in terms of messages Dic-
tionary understands. (Recall the messages required to list all employees
with salaries less than 20,000, for example.) A better scheme would be to
create a new class, like EmployeeDatabase, that would respond to mes-
sages more appropriate for our application. A set of messages might be
the following:
newEmployee

48 The Language
create a new employee record.
upDate: aNumber
update the fields for the employee with the given number.
list: aBlock
print out information on all employees that satisfy the condition given
by the argument block.
A typical session using this class might appear as follows:
employeeDatabase ~ EmployeeDatabase new
employeeDatabase newEmployee
id: 2431
name: Fred Brown
position: president
salary: 68020
employeeDatabase update: 2431
name (Fred Brown):
position (president):
salary (68020): 72000
employeeDatabase list: [:x Ix position =#president]
name: Fred Brown position: president salary: 7200
The new database must still retain the information somewhere. One
way is to maintain an internal dictionary. This can be established at the
time an instance of the database class is created, using the special seman-
tics of the new message.
Class EmployeeDatabase
I records I
[
new
records ~ Dictionary new
The messages upDate: and list: merely pass appropriate commands to
the dictionary.
upDate: idNumber
(records at: idNumber) upDate
list: condition
records do: [:x I (condition value: x) ifTrue: [ x print] ]
The message new Employee requires an additional prompt to return
the employee number.
\ i i
\
: ~
:'
~ \ -
.'
A Simple Application 49
newEmployee I newNumber I
lid: I printNoReturn.
newNumber ~ smalltalk getString aslnteger.
records at: newNumber put: EmployeeRecord new
The exercises propose various further extensions that could be made
to this application.
Saving Environments
Updating an employee database is not something you do once and then
never again; rather it must be done periodically, as new employees are
hired or retire. One way to update an employee database is to record the
final values for the database on a slip of paper. Before the start of the next
session you initialize the database by initializing each entry. This may be
somewhat unsatisfactory; slips of paper tend to get lost. Since computers
usually have a much better memory for such things, a good alternative is
to use the Little Smalltalk system to save and restore environments. We
use the term environment to denote the set of all objects accessible at any
one time. The command
)s filename
saves a representation of the current environmerh in the indicated file. I In
response to this command, a message indicating the number of bytes
written to the file will be printed. These files tend to be rather large; too
many of them can clutter your directory.
After saving an environment, you can then continue execution or exit
the Little Smalltalk system by typing control-D. A saved environment can
later be restored by typing the command
)1 filename
This command loads the environment saved in the indicated file. Notice
that in doing so, it totally erases the environment that existed before the
)1 command was issued, replacing it with the restored environment. In
response to this command, a message indicating the number of bytes read
in will be produced. This figure should match the figure written after the
)s command. All names that denoted accessible objects before the )s COffi-
1. The )s and )1 commands do not work on all machines on which the other portions
of the Little Smalltalk system will operate. Check with your system manager, or experiment
yourself, to see if they work on your system.
50
EXERCISES
\
~ \ - .
The Language
mand are now valid, and the user can continue as if neither the )s nor the
)1 commands had been issued.
Note that the same environment can be loaded many times. Acommon
use for this feature is for users to save an environment containing their
favorite classes and objects that they have created. This environment can
then be quickly loaded, and the classes will be available without having to
issue )i commands for each one.
1. Add messages to delete employees from the database.
2. Is it necessary to have both the messages initialize and upDate in Em-
ployeeRecord ? Can one be replaced with the other? What changes
would the user notice?
3. As it stands, the application is rather lax about error checking. Add
checks to make sure a new employee record does not override an older
one and that a request to update specifies a valid employee identifi-
cation number.
4. In the scheme described in this chapter, identification numbers are
different from the other fields in the employee record. They are kept
oilly as keys in the database and not as part of the record, for example.
Change the class EmployeeRecord so that it maintains the identifi-
cation number as part of the record. What changes are then required
in EntployeeDatabase ?
5. Instead of maintaining an instance of Dictionary in Employee-
Database, one could make it a subclass of Dictionary. How would
this change the methods for this class? What are the advantages and
disadvantages of both schemes?
» » > : » > : > : ; : ~ >
/ /// / / / / //
'"'"'"'''' '"'"""'""'- "-
{{{{{{{{{{{{{{{{{{{{{{{{{i
-}}}}}}}}}}}}}}}}}}}}}}}}}
««««{«({{««««{,
»»>::»»>
/////////,/
'"'"'"""'""-, ""'"',,', ""
~ {{{{{{{{{{{{{{{{{{{{{H{{
t}}}}}}}}}}}}}}}}}}}}}}}}}
:«««««««««««1
))))))))))))))))\))))))
CHAPTER
6
Primitives, Cascades,
and Coercions
51
\ ~
\ \
i
) ~ \ ~ -;;,
52
Cascades
The Language
Suppose you wanted to send a number of messages to the same object.
For example, suppose you wanted to create a new instance of class Bag
and to initialize it with the values 0 and 1. One way to do this would be
to use a temporary variable as in:
i ~ B a g new
i add: 0
i add: 1
Another way is to use a cascade. A cascade is a type of Smalltalk
expression.' It is written as an expression followed by a semicolon followed
by another expression without a receiver. When a cascade is evaluated, the
first expression is computed, and then used as the receiver for the second
expression. The result of a cascade is not the result of the second expression
but the result of the first expression. In other words, the result of the second
expression is ignored and discarded. Since a cascade is an expression, it
can be used to form another cascade, and so on, to any desired level. The
result of a cascade of any depth is always the result of the first expression.
Thus, for example, you can create a new Bag and initialize it all with one
expression, as in:
Bag new; add: 0 ; add: 1
The receiver for both the messages add: a and add: 1 is the result of the
expression Bag new, as is the result of the entire expression.
There are several advantages to using cascaded expressions. Since
there is no necessity to name the intermediate object, a cascade obviates
the need for a great many temporary variables. Also, since the entire se-
quence is an expression, it can be used anywhere expressions are legal,
for example, as an argument. Finally a cascade can often be used in place
of parentheses to separate two keyword messages or to apply one message
to another of lower precedence. For example, to print the result yielded
by a keyword message you could write:
Integer respondsTo: #+ ; print
in place of
(Integer respondsTo: # +)print
1. Note that although similar concepts appear in both the Little Smalltalk system and
the Xerox Smalltalk-80 programming environment, the syntax and meaning of cascades and
primitives are different in the two systems. Appendix 5 describes howcascades and primitives
are handled in the Xerox system.
Primitives
'.
.\.
Primitives, Cascades, and Coercions 53
At this point you may be wondering how anything ever gets accomplished
in Smalltalk. As described so far, actions are produced by one object send-
ing a message to another object. In response to this message, the second
object may in turn send still other messages to more objects. If this were
all there were to the language, there would seem to be a sort of infinite
regress preventing any substantial action from taking place.
The solution to this problem is yet another form of expression, called a
primitive. Aprimitive can be thought of as a system call, permitting access
to the underlying virtual machine. Syntactically, a primitive is denoted by
an angle bracket followed by a name followed by a list of objects followed
by a closing angle bracket, for example:
<lntegerAddition i j >
Alternatively, every primitive is also assigned a number. The primitive
name can be replaced by the keyword "primitive" and this number. Thus,
since the integer addition primitive is number 10, the above expression
could also be written as:
<primitive 10 i j >
However, the second form is less representative of the operation being
performed. Primitives are chiefly used in class descriptions and should al-
most never by typed directly at the command level. In any case, only the
second form (expressing the desired primitive by number) is recognized
at the command level.
Note that a primitive call is not the same as a message passing, although
the differences are largely hidden from the user. There is no notion of a
"receiver" in a primitive expression, and a primitive cannot send further
messages in producing its response. For this reason most primitive op-
erations, as the name suggests, are very simple. A table of primitives is
given in Appendix 4. In general the value nil is returned, and an error
message produced, if arguments to a primitive are not correct or the prim-
itive cannot produce the desired result.
Aportion of the class description for the class Radian is given in Figure
6.1. This class uses the primitives Sin and Cos to compute the value of
the trigonometric functions on radian inputs. Notice how the class Radian
responds to the message printString by concatenating the string represen-
tation of its value with the literal string "radians." Thus the message IfX
print," if x is a radian with value 0.85, will produce the message "0.85
radians." This technique is used in many classes to provide more inform-
ative printed representations of objects.
There is a temptation on the part of many novice Little Smalltalk users
to use primitives in place of Smalltalk expressions in the name of lleffi_
54
Figure 6.1 0 The class Radian
ClassRadian :Magnitude
I value I
[
The Language
sin
tan
cos
new: x
value ~ x asFloat
< arg
t value < arg asFloat
= arg
t value = arg asFloat
t <Sin value>
t <Cos value>
t self sin / self cos
asFloat
t value
printString
t value asString, I radians
l
ciency." This should be avoided. Such gains in efficiency are usually neg-
ligible, if they exist at all. More importantly, the notion of primitive is
really just a pragmatic device permitting some operations to be specified
that could not otherwise be given in Smalltalk. The syntax is a bit obscure,
and the notion does not fit quite smoothly into the object-oriented frame-
work of the rest of Smalltalk. Thus primitives should be used only as a
last resort, only after a great deal of thought as to the alternatives, and
only in extremely simple methods that put a more lfSmalltalk-like" cloak
over the primitive call.
Numbers
The class hierarchy for numbers could be described as follows:
Object
I
Magnitude
------- ~
Number Char
/ ~
Integer Float
\ \
\
\
.'
),
:'
....~
Primitives, Cascades, and Coercions 55
Each of the classes Integer, Float, and Number implements methods for
the arithmetic operations. The classes Integer and Float perform opera-
tions only for arguments of their own type. If an argument is not of the
correct type, the message is then passed to the superclass, Number. For
example.,the method for + in the class Integer looks something like the
f o l l o w i n ~ :
+ aNumber
l' <SameTypeOfObject self aNumber>
ifTrue: [ <lntegerAddition self aNumber> ]
ifFalse: t super + aNumber]
The primitive SameTyPeOfObject tests whetherthe two objects given
as arguments are instances of the same class. Since the pseudo variable
self is an instance of the class Integer, in this context the use of this
primitive can be thought of as equivalent to:
aNumber isKindOf: Integer
although the primitive expression, since it does not require any further
message passing, is somewhat more efficient.
2
If the argument is of the
correct class, primitive IntegerAddition produces the integer sum of the
two arguments. If the argument is not an instance of class Integer, the
message If +11 is passed to. the superclass, namely Number. In the class
Number the following methods appear:
maxtype: aNurnber.
l' <GenerqlityTest self aNumber>
ifTrue: [self]
ifFalse: [aNumber coerce: self]
+ ?Number
i (self maxtype: aNumber) + (aNumber maxtype: self)
To uiIderstand these methods, consider that a hierarchy of number
classes can be defined, consisting of Integer at the lowest level, followed
by Float, then followed by any others (including user-defined classes).
When presented with two objects of different levels in the hierarchy, the
class Number chooses the object with the more general class and passes
to it the message coerce: with the other object as an argument. For example
typing i + 3.5 results in the message coerce: 2 being passed to the object
3.5. The class Float, and any user-defined dasses, must implement a
method for this message.
2. The use of the primitive here is justified on the grounds that arithmetic operations
are used much more frequently than other messages. Even so, some regard this as a weak
argument and would advocate the more direct and obvious Smalltalk expression over the
less clear primitive call.
56
EXERCISES
The Language
To find the most general form for the operation you use the message
maxtype:. The method for this message uses the primitive GeneralityTest,
which returns true if the first argument is of a more general class than the
second, and false otherwise.
3
The method for maxtype: therefore either
returns its first argument or coerces the argument into being of the class
represented by the second argument:
coerce: aNumber
i aNumber asFloat
When Number has coerced both arguments into being the same type,
the original message is then passed back to the modified objects. Assuming
the response to coerce: was as expected, the objects should now be able to
respond correctly to this message, and the expected result is finally pro-
duced.
1. One advantage cited for cascaded expressions was that several Small-
talk statements could be combined together into one expression. For
example the initialized Bag discussed in the text could be used as an
argument to another object as follows:
anObject foo: (Bag new; add: 0; add 1)
How else might this be done in a single expression without using
cascading? You can use temporary variables, if you wish.
2. What will the result of typing the following expression be? Explain
why.
2 + (3 pri nt) ; + 4
3. Examine the class descriptions for the classes Object, Magnitude,
Number, Integer, and Float. Explain in detail how radians respond
to the messages < = and > =.
4. Rewrite the methods for = and < in class Radian so that radians can
be compared only to other radians.
5. Does the class Integer need to provide a method for coerce:? Why or
why not? How about the class Number?
3. There is a problem here in determining the relative generality of two user-defined
classes, or even the generality of user-defined classes and known classes, such as Float. One
of the projects described at the end of the book invites the student to examine this problem
and produce more general solutions.
'.
,
\
\
; ~ ~ ( }
) ~ '.0:-
Primitives, Cascades, and Coercions 57
.6. An alternative to having instances of class Radian maintain their value
in an instance variable would be to make the class a subclass of class
Float. Discuss the advantages and disadvantages of this approach.
7. Recall that in the hierarchy of number classes all other classes have a
higher ranking then any of Integer or Float. Assume that the class
Number contains the following method
t Complex new; imagpart: self
That is, in response to the message i, a number will create a new
instance of the type Complex and initialize it with the current object.
Using this method, define the class Complex used to manipulate com-
plex numbers. Your class should implement methods for the following
messages:
new
realpart:
imagpart:
coerce:
+
*
printString
Set both the imaginary and real portions of the complex
number to zero.
Define the real part of the current number to be the
argument.
Define the imaginary part of the current number to be
the argument.
Coerce a non-complex, returning an equivalent complex
number with zero imaginary part.
Complex addition.
Complex multiplication.
produce a printable representation of the number.
Test your class description by typing several example expressions in-
volving complex numbers.
8. A useful control structure in many programming languages is a multi-
way switch statement, which permits the selection of one out of several
possibilities based on the value of some selection key. Using cascades
and blocks we can implement a multiway switch in Smalltalk. The
class Switch will use the message new: both as a creation message
and to assign the switch selection key. The message case:do: will com-
pare the first argument to the selection key, and, if equal, the second
argument (a block) will be evaluated. The message test:do: uses blocks
for both arguments. The first argument, a one parameter block, is
evaluated using the selection key; if it returns true, the second argu-
ment is evaluated. A flag is maintained by each instance of Switch
indicating whether any condition has been satisfied. The message de-
fault: executes the argument, a block, if no previous condition has been
met.
58
"\
.\.
The Language
For example, the following statement uses a variable suit representing
the suit of a card from a deck of cards. It places into color a string
representing the color of the card.
Switch new: suit;
case: #spade do: [ ;
case: #c1ub do: [ ] ;
test: [:x I (x = #diamond) or: [x = #heart] ] do: [ ;
default: [ self error: 'unknown suit I , suit]
Provide a class description for Switch.
\ \ \
"
~ } .
;}
s ~ \
////// / / ~ / /
""""""", '"""""'""". "-
{{{{{{{{{{{H{{{{{{{{{{{{i
.}}}}}}}}}}}}}}}}}}}}}}}}}"
««({««««««««(,
: » » > ~ : > . : : > . : . » >
//////////
,,"""''''''''''''''''''''''''''
~ {{Hi{U{{{{{{{{{{{{{{{{{
n}}}}}}}}}}}}}}}}}}}}}}}}
[««{««««««({{«;
i))))))))))))))))))))))
CHAPTER
7
A Simulation
59
60

\ \;-
\
), ~ ~
.'
~ -;r
The Language
The programming paradigm employed in Smalltalk, that of a large number
of independent objects communicating via message passing, is particularly
suitable for constructing simulations of processes that can be described
as the interaction of a finite number of events. Each object can represent
some object in the simulation model, with the local memory of the object
maintaining information about the state of the model object. Messages
correspond to interaction between the simulation objects. Indeed, the lan-
guage was developed largely with just such applications in mind. This
chapter will illustrate the construction of a simple simulation describing
the operation of an ice cream store. The reader interested in more extensive
simulation techniques can consult some of the references listed at the end
of this chapter.
The Ice Cream Store Simulation
In writing a simulation, such as our simulation of an ice cream store, the
first question to ask is what the different objects in the simulation should
represent and what functionality they should possess. At its simplest, our
simulation must involve at least two classes of objects: a class representing
the actions of the various customers that come to the store and a class
representing the actions of the store itself in response to the requests of
the customers.
The application we will describe is known as a discrete, event-driven
simulation. This means that the actions of the simulation will revolve
around the recording and processing of a finite number of individual
events. An event is simply the occurrence of some action that is important
in the context of the simulation. Each event is marked with a time at which
the event should take place. A "clock" records the current "time," which
need not be related in any way to conventional time; a simple counter will
suffice. As time progresses, the occurrence of each event controls the se-
quence of further actions in the simulation.
Our first, and very simple, attempt at simulating an ice cream store
will illustrate these concepts. Apending event is defined as any event that
has. not yet taken place. Assume, first, that there is never more than one
pending event. At the moment we need not concern ourselves with how
each event is to be encoded, but only that it can be represented by an
object. We can isolate the actions of the simulation that are independent
of any particular application in an abstract superclass called Simulation.
Particular simulations, such as in the ice cream store, will then be sub-
classes of Simulation.
The first version of the class Simulation is shown in Figure 7.1. The
class maintains the current "time" in the variable currentTime. The mes-
sage proceed will indicate that the next action should take place. Since the
\
~ \ -
\
~ \ -
A Simulation 61
Figure 7.1 0 The class Simulation (version 1)
Class Simulation
I currentTime nextEvent nextEventTime I
[
new
currentTime ~ 0
time
t currentTime
addEvent: event at: eventTime
nextEvent ~ event.
nextEventTime ~ eventTime
proceed
currentTime ~ nextEventTi me.
self processEvent: nextEvent
actual interpretation of an event will differ from one simulation to the
next, proceed leaves the task of interpreting the event to a subclass by
passing the message processEvent: to the pseudo variable self. The message
processEvent: must therefore be recognized in each subclass.
Having described the framework for keeping track of the simulation
"bookkeeping," we can go on to describe the actions of our ice cream store.
We start with a simple model in which customers arrive, order some num-
ber of scoops of ice cream, and leave. Each scoop of ice cream produces
some amount of profit for the store. At the end of the simulation period
we will want to know the profit.
It is, however, not sufficient to merely describe what actions take place
in the store; we must also describe how often those events should take
place. For example, how often do customers arrive, how many scoops of
ice cream will each customer order, and so on. We could make a simple
assumption, such as a new customer will arrive every two minutes and
order three scoops of ice cream. Such a deterministic assumption, how-
ever, defeats the necessity for the simulation at all since we can easily
predict the outcome: in 15 minutes seven customers will arrive and order
21 scoops of ice cream. Instead, it is more interesting (to say nothing of
being more realistic) to define the outcome of an event in terms of a random
value chosen from a selected distribution.
For example, let us suppose that at each instance the next customer
will appear at a time uniformly chosen from the numbers 1 to 5. The
phrase "uniformly chosen" means that each outcome (or each time) is
equally likely, and thus we can use a random number generator to select
62 The Language
Figure 7.2 0 A portion of the class IceCreamStore (version 1)
Class IceCreamStore :Simulation
I profit rand I
[
new
profit ('- O.
rand ('- Random new.
rand randomize.
self scheduleArrival
scheduleArrival
self addEvent: Customer new
at: (self time + (rand randInteger: 5»
reportProfits
('profits are I, profit) print
a value in this range to represent the time for the next arrival. Letting
IceCreamStore be the class representing our simulation of the store, this
could be computed as shown in the method for the message scheduleArrival
in Figure 7.2. Note that the instance variable rand is initialized to be an
instance of the random number generator, and that during initialization
the first customer is scheduled. The message randomize is used to insure
that a new random sequence is generated each time we invoke the sim-
ulation.
Notice we have chosen to represent events by an instance of class
Customer. In our first simulation very little functionality is required of
the customer. Each customer must arrive and upon arrival decide the
number of scoops of ice cream he or she will order. Thus the class shown
in Figure 7.3 suffices for customers.
The only remaining part of our simulation is deciding how the class
IceCreamStore should respond to the message processEvent:. Since the
event is an instance of class Customer, all that is required is determining
the amount of ice cream to be dispensed (and thus the profit to be made)
and scheduling the next customer. The latter requirement may not be
obvious, but is important. For our simulation to work, each event must
generate some number of other events. The decision about where and when
each event should schedule the next is not always obvious, but must be
made someplace.
A Simulation
Figure 7.3 0 The class Customer (version 1)
Class Customer
I rand I
[
new
rand ~ Random new.
rand randomize
numberOfScoops I number I
number ~ rand randlnteger: 3.
('customer has " number I I scoops I) print.
l' number
63
Let us assume that each scoop of ice cream generates seventeen cents
profit for the store. The response to the message processEvent: can then
be represented as follows:
processEvent: event
('customer received at I, self time) print.
profit ~ profit + (event numberOfScoops * 0.17 ).
self scheduleArrival
To determine how much profit might be produced in 15 minutes, we could
then run our simulation as follows:
store ~ IceCreamStore new
[store time < 15] whileTrue: [store proceed]
customer received at 1
customer has 1 scoops
customer received at 2
customer has 3 scoops
customer received at 5
customer has 1 scoops
customer received at 8
customer has 1 scoops
customer received at 11
customer has 1 scoops
customer received at 16
customer has 2 scoops
store reportProfits
profits are 1.53
64 The Language
Now, having produced our first simulation, let us go back and examine
some of the assumptions we made to decide if they are really accurate. Do
customers really arrive with a uniform distribution? Do they always arrive
individually? Do they really order some number of scoops with a uniform
probability?
Let us tackle the last question first. Suppose we observe a real ice
cream store for some period of time and note that 65% of the time cus-
tomers will order one scoop, 25% of the time they order two scoops, and
only 10% of the time do they order three scoops. This is certainly a far
different distribution from the one given by our assumption that all three
outcomes are equally likely. In order to simulate this behavior, we must
generate a random integer that returns one 65% of the time, two 25% of
the time, and three 10% of the time. A distribution of values such as we
have described is known as a weighted discrete probability. One way to
generate a random value satisfying our requirements is to generate a uni-
form random number between 1 and 100. If this uniform value is less than
or equal to 65, we return 1; if less than or equal to 90, we return 2;
otherwise, we return 3. We can generalize this to work for any weighted
distribution we like, producing the class DiscreteProbability shown in
Figure 7.4. In response to the message next, an instance of this class will
return a value between 1 and the size of the weights array, distributed
according to the weights given.
Figure 7.4 D The class DiscreteProbability
(I ass DiscreteProbabil ity
I weights rand max I
[
defineWeights: anArray
weights ~ anArray.
rand ~ Random new.
rand randomize.
max ~ anArray inject: 0 into [:x :y I x + y]
next I index value I
value ~ rand randlnteger: max.
index ~ 1.
[ value> (weights at: index) ]
whileTrue: [ value ~ value - (weights at: index).
index ~ index + 1 ].
j index
A Simulation 65
The distribution of ice cream orders was obtained by obsenring a large
body of customers. So we can argue whether the number of scoops an
individual will order should be part of the protocol for the Customer class
(since the customer is issuing the order) or for the IceCreamStore class
(since the distribution is taken from observations of ice cream store cus-
tomers as a group). The last simulation illustrated the first Variation. The
following simulation will illustrate the second variation.
Let us alter the assumption that customers arrive one by one; since it
is a social process, people tend to eat ice cream in groups. Each instance
of class Customer will be changed, therefore, to represent a group of
individuals. Upon creation, each instance will determine its group size,
which will thereafter be returned in response to a request via the message
groupSize. Given these changes, our second simulation can be given as
shown in Figure 7.5.
Suppose we complicate things now by adding an inside dining area to
our ice cream store. There are several more factors to consider. Whereas
formerly we assumed we could accommodate as many customers as would
arrive in any particular time period, now we can only accommodate those
customers who can find chairs. In this new situation, the sequence of events
affecting a single customer or a group of customers is now more compli-
cated than the previous case, where the only event of importance from the
customer's point of viewwas the receipt of the ice cream. Nowthe following
sequence will take place:
1. Some group of customers will arrive. If there are not enough chairs,
they will immediately leave. Otherwise, they will take seats and start
to look at the menu.
2. Later the group of customers will place an order and receive their ice
cream.
3. Still later, the group will have finished their ice cream and will leave.
Furthermore. we can now have several events happening simultane-
ously. One group of customers can be eating their ice cream, while another
is ordering, and a third is just arriving. Thus, the basic assumption that
there is always just one pending event is no longer valid. Now the class
Simulation must be altered to keep a queue of pending events. In response
to the message proceed, the event with the next smallest time marker is
removed from the queue and initiated. One convenient data structure to
maintain both the pending events and their time for initiation is a dic-
tionary using the time as a key and the event as the value. However, two
events can happen at the same time (for instance, one group can order
while another arrives). Therefore the value of the dictionary cannot be
simply a single event, but must be a set of events. Our revised class Sim-
ulation is shown in Figure 7.6.
Events are now more complicated. We must .remember not only a
66 The Language
group of customers but also what state they are in: whether they have just
arrived, are about to order, or are about to leave. One way to do this is to
store as an event a block which when evaluated will move the customer
to the next state. Recall that a block evaluates in the context in which it
IS defined and not until passed the message value. Thus, for example, the
protocol for scheduleArrival might be given as follows:
scheduleArrival I newCustomer I
newCustomer ~ Customer new.
self addEvent: [self customerArrival: newCustomer]
at: (self time + (rand randlnteger: 5»
Figure 7.5 0 Ice cream store simulation (version 2)
Class IceCreamStore :Simulation
I profit rand scoopDistribution I
[
new
profit ~ O.
rand ~ Random new.
scoopDistribution ~ DiscreteProbability new
scoopDistribution defineWeights: #(65 25 10).
self scheduleArrival
scheduleArrivaI
self addEvent: Customer new
at: (self time + (rand randlnteger: 5»
processEvent: event
Ccustomer received at I, self time) print.
profit ~ profit + «selfscoopsFor: event groupSize) * 0.17).
self scheduleArrival
scoopsFor: group I number I
number ~ O.
group timesRepeat:
[number ~ number + scoopDistribution next].
('group of I, group, I have I I number I I scoops I) print.
t number
Program Continued
\
~ . ~ -
A Simulation
reportProfits
('profits are I, profit) print
Class Customer
1 groupSize I
[
new
groupSize <E- (Random new randomize) randlnteger: 8
groupSize
t groupSize
67
A new customer is created and placed in the temporary variable new
Customer. A block is then installed in the event queue. Since this block
references the temporary variable newCustomer, the temporary will be
retained (i.e., the storage it uses ·will not be reclaimed) as long as the block
exists. However, each time the message scheduleArrival is received, a new
instance of Customer will be created. The processing indicated by the
block will not take place until the block is evaluated using the message
value. This takes place when the event is processed:
processEvent: event
event value.
self scheduleArrival
Figure 7.6 0 The class Simulation (version 2)
Class Simulation
I currentTi me eventQueue I
[
new
eventQueue ~ Dictionary new.
currentTi me ~ 0
time
t currentTime
addEvent: event at: eventTime
Program Continued
"\
~ . } -
68
\.
The Language
c= ---l
(eventQueue inciudesKey: eventTime)
ifTrue: [(eventQueue at: eventTime) add: event]
ifFalse: [eventQueue at: eventTime
put: (Set new; add: event)]
addEvent: event next: timelncrement
self addEvent: event at: currentTime + timelncrement
proceed I minTi me eventset event I
minTime ~ 9 9 9 9 9 .
eventQueue keysDo:
[:x I (x < minTime) ifTrue: [minTime ~ xl].
currentTime ~ minTime.
eventset ~ eventQueue at: minTime ifAbsent: [t nil].
event ~ eventset first.
eventset remove: event.
(eventset isEmpty) ifTrue: [eventQueue removeKey: minTime].
self processEvent: event
When the block created in scheduleArrival is evaluated, it will send
the message customerArrival: to the pseudo variable self, that is, to the
simulation object. Upon arrival, if there are a sufficient number of chairs,
the customers sit down and order, otherwise they leave. Let us use an
instance variable remainingChairs to represent the number of free chairs
at any point. The time between arriving and ordering will be a random
value from one to three. The protocol for customerArrival: can then be
given as follows:
customerArrival: customer I size I
size ~ customer groupSize.
('group of size 1, size, I arrives
'
) print.
(size < remainingChairs)
ifTrue: [remainingChairs ~ remainingChairs - size.
'take chairs, schedules order' print.
self addEvent: [self customerOrder: customer]
next: (rand randInteger: 3).
]
ifFalse: ['finds no chairs, leaves· print]
Notice that again a block has been used to represent the next event.
When evaluated, this block will pass the message custonlerOrder:. The
protocol for this message is as follows:
"\
~ .
"\
\
~ \ ..~
.\ ):
A Simulation
customerOrder: customer I size numScoops I
size ~ customer groupSize.
numScoops ~ O.
size timesRepeat:
[numScoops ~ numScoops + scoopDistribution next].
('group of size I, size, I orders I , numScoops , I SCOOpSl) print.
profit ~ profit + (numScoops * 0.17).
self addEvent:
[self customerLeave: customer]
next: (rand randInteger: 5)
69
Once more the time between a group of customers ordering and leaving
is determined by a random value chosen between 1 and 5. When the
customers finally do leave, they relinquish their chairs.
customerLeave: customer I size I
size ~ customer groupSize.
('group of size I, size, I leaves
l
) print.
remainingChairs ~ remainingChairs + customer groupSize
We will make one final change to illustrate how our simulation can be
made even more realistic. In practice, few random events ever occur with
uniform probability. More often, other distributions, such as a Bernoulli
distribution or a Poisson distribution, are observed to model a process.
One very common form is the Normal distribution, which is characterized
by values clustering around a mean, with the chances of a value decreasing
exponentially the farther they move from the mean. Figure 7.7 shows one
class that can be used for generating random values with a normal dis-
tribution. No attempt is made to motivate the algorithm used in the method
for the message next; an interested reader can refer to the end of this
chapter fDr additional literature.
Given the ability to produce randomvalues from a normal distribution,
we can change our assumption about customers arrivals to be more re-
alistic. For example, we could assume customers arrive in a normal dis-
tribution with a mean of 3 minutes and a standard deviation of 1 minute.
Figure 7.8 shows the class header and the protocol for the initialization
message new and the message scheduleArrival incorporating these changes.
In the method for scheduleAnival we have also incorporated a "closing
time" by adding events corresponding only to customers who arrive before
some fixed limit. After closing time, no new customers will arrive, but the
customers waiting to order and waiting to leave will still be processed.
This is so that the event queue can be flushed out and the simulation ter-
minate in a clean fashion.
Other random values used in the simulation could be modified to use
a different distribution by making changes such as the ones we have il-
lustrated for the arrival time distribution.
70 The Language
We end with an example session of our final simulation:
Figure 7.7 0 The class Normal
Class Normal :Random
I mean deviation I
[
new
self setMean: 1.0 deviation: 0.5
setmean: m deviation: s
mean m.
deviation s
next I v1 v2 sui

[s > = I] whileTrue:
[v1 (2 * super next) - 1
v2 (2 * super next) - 1.
s v1 squared + v2 squared ].
u (- 2.0 * s In / s) sqrt.
t mean + (deviation * v1 * u)
Figure 7.8 0 The class IceCreamStore (version 3)
Class IceCreamStore :Simulation
I profit arrivalDistribution rand scoopDistribution remainingChairs I
[
new
profit O.
remainingChairs 15.
rand Random new.
Normal new:
setMean: 3.0 deviation: 10.
scoopDistribution DiscreteProbability new:
defineWeights: #(65 26 10).
self scheduleArrival
scheduleArrival I newCustomer time I
newCustomer Customer new.
time self time + (arrivalDistribution next).
Program Continued
~ ~ . \
\
\ ~ } > ):
A Simulation
(time < 15) ifTrue: [ self addEvent: [ self customerArrival: newCustomer]
c:lt: time]
store ~ lceCreamStore new
[stQre time < 60] whileTrue: [store proceed]
event received at 3.46877
group of size 8 arrives
takes chairs, schedules order
event re<;eived at 5.81336
group of size 8 arrives
finds no chairs, leaves
event received at 6.46877
group of size 8 orders 11 scoops
event received at 146877
group of size 8 leaves
event received at 8.91228
group of size 1 arrives
takes chairs, schedules order
event received at 10.9123
group of size 1 orders 1 scoops
event received at 10.9499
group of size 7 arrives
takes chairs, schedules order
event received at 11.8463
group of size 5 arrives
takes chairs, schedules order
event received at 12.0194
group of size 2 arrives
finds no chairs, leaves
event received at 12.8463
group of size 5 orgers 6 scoops
event received at 12.9123
group of size 1 leaves
event received at 12.9499
group of size 7 orders 13 scoops
event received at 13.8077
group of size 7 arrives
finds no chairs, leaves
event received at 14.6301
group of size 5 arrives
finds no chairs, leaves
71
Program Continued
'.
,
\
\
;}
"
72 The Language
event received at 16.9499
group of size 7 leaves
event received at 17.8463
group of size 5 leaves
store reportProfits
profits are 5.27
Since Smalltalk objects can be created, removed, and in general placed
in a one-to-one correspondence with objects in the abstract model being
simulated (the objects representing the customer groups, for example). It
is relatively easy to take any model expressed in the discrete event-driven
form and enunciate it in Smalltalk. Similarly the development of new
simulations is simplified by the inheritance of common behavior from class
Simulation. Finally, once a simulation has been developed, it is easy to
modify, for example, replacing a value generated with a normal distri-
bution with one generated according to a Bernoulli distribution.
Further Reading
A good introduction to the concepts of simulation can be found in (Mary-
anski 80). A more theoretical treatment is given in (Zeigler 76). The defin-
itive description of the Smalltalk-80 language (Goldberg 83) illustrates
many more extensive simulations in Smalltalk. Other programming lan-
guages designed for simulation include Simula (Birtwistle 73), Demos (Birt-
wistle 79), and GPSS (Greenberg 72). The algorithm for computing the
normal distribution is taken from Knuth (Knuth 81, Vol. 2). A large col-
lection of references to computer games and simulation exercises is found
in (Gibbs 74).
EXERCISES
1. Deciding when to use subclassing and when to use an instance variable
is not always easy. An argument can be made that DiscreteProbability
should really be a subclass of Random, such as the following:
Class DiscreteProbability :Random
I weights max I
[
Program Continued
"\
.':0:.
A Simulation
defineWeights: anArray
weights ~ anArray.
max ~ anArray inject: 0 into: [:x :y I x + y]
next I index value I
value ~ super randlnteger: max.
index ~ 1.
[value> (weights at: index)]
whileTrue: [value ~ value - (weights at: index).
index ~ index + 1].
t index
73
Look at the class description for Random, in particular the response
. to the message randlnteger:, and then describe why this class descrip-
tion will not produce the desired result.
2. An alternative method of defining a discrete probability is to provide
the actual sample space in a collection. For example, suppose a group
of boys are observed to have heights represented by the array #(60 54
60 62 50). We can then ask for the height of a randomly selected boy.
The following shows how a class SampleSpace might be used for this
purpose:
sample ~ SampleSpace new; define: #(60 54 60 62 50)
sample first
60
Producy a class description for SampleSpace.
3. If written in a manner analogous to DiscreteProbability, the class
SampleSpace defined in the last exercise is said to provide Ilrandom
selection with replacement. II The alternative, random selection without
replacement, is frequently more useful. For example, a sample space
might represent a deck of cards, and a random selection, the choosing
of a card. This card should then be thought of as being removed from
the deck and not available for return in subsequent selections.
Describe how to modify the class SampleSpace to provide random
selection without replacement.
\.
\
.~ .
\
:"': .
///////////
'""-" '"'"'""'.. '"'""'" "-
{{{{{H{{{{{{{{{{{{{{{{{{,
-}}}}}HH}}}}}}}}}}}}}}}}
««««««««««{«,
/// /// //// /
"'- '"",,"'.. "'.. '""'.. '''-. '"'"
'{{{{{H{{{{ {{{{{{{{{{{{{{
r}}}}}}}}}}}}}}}}}}}}}}}}}
((((((((((((i
))))))11111)))))))))\\)
CHAPTER
8
Generators
74
\ \
\
'.
; ~
~ ~ .\ ~ \
Generators 75
In Little Smalltalk, the term generator describes any object that represents
a collection of other objects and that responds to the following two mes-
sages:
first The response should be an element of the collection, or the special
value nil if there are no elements in the collection.
next The response should be another element in the collection, or nil if
there are no more elements in the collection.
For example, instances of the standard data structures, such as Array,
String, or Bag, can all be considered to be generators. Instances of Array
or String return the element stored in their first subscript position (if they
have at least one subscript position) in response to the message first. On
subsequent next messages they respond with the remaining elements in
order. This functionality is provided by class ArrayedCollection, using an
instance variable current (Figure 8.1.)
Some data structures, such as instances of the class Bag, do not possess
a "natural" ordering, and thus the order in which elements are produced
in response to first and next messages is not defined, other than that all
elements are eventually produced and no element is produced more than
once.
Notice that nothing is said about how a generator produces the object
to be yielded in response to one of these messages. Some objects, such as
instances of Bag or Array, maintain their collections in memory, and thus
the response to first and next is merely to enumerate their elements. In-
stances of File are similar, only the values are retrieved from an external
disk as required. Other generators, such as instances of Interval, maintain
only the information necessary for generating each new element as re-
quired, and that recompute each new element on demand (Figure 8.2).
Indeed, the list of elements represented by instances of class Random can
Figure 8.1· 0 A portion of the class ArrayedCollection
I current I
[
first
current ~ 1.
i (current < = self size) ifTrue: [ self at: current]
next
current ~ current + 1.
i (current < = self size) ifTrue: [ self at: current)
76
"\ \
"
"
.,;:} ~ \ ~ \
The Language
Figure 8.2 0 A portion of the class Interval
Class Interval :SequenceableCollection
I lower upper step current I
[
inRange: value
i (step strictlyPositive)
ifTrue: [ value between: lower and: upper]
ifFalse: [value between: upper and: lower]
first
current ~ lower.
i (self inRange: current) ifTrue: [ current]
next
current ~ current + step.
i (self inRange: current) ifTrue: [ current]
be considered to be infinite in length, and thus cannot be stored entirely
in memory.
From the point of view of the message passing interface, there is no
distinction between classes that iterate over their elements in memory and
classes that produce new elements on demand. Even in cases where the
sequence to be produced in response to first and next is finite, there may
be advantages to computing elements as needed rather than all at once
when the object is defined.
An example will illustrate how generators assist problem solving in
Smalltalk. Consider the problem of producing prime numbers. By defi-
nition, a prime number is a value having only two divisors, itself and 1. A
generator for prime numbers will produce the first prime value (namely
2) when offered the message first, and successive prime numbers in re-
sponse to each next message.
If a number n divides a number 111, then the prime factors of n must
also divide nt. Thus, to tell if a number nl is a prime, we need not test all
values less than 111, only those values that are prime. Therefore a simple
generator for primes can be constructed by merely retaining the previously
generated primes in a Set. As each new value is requested, an object
representing the last prime produced is incremented and tested until a
value having no factors is found. The new value is then inserted into the
set and returned.
\
.'l:.
Generators
Class Primes
I prevPrimes lastPrime I
[
first
prevPrimes ~ Set new.
prevPrimes add: (IastPrime ~ 2).
l' lastPrime
next
[ lastPrime ~ lastPrime + 1.
self testNumber: lastPrime ] whileFalse.
prevPrimes add: lastPrime.
l' lastPrime
testNumber: n
prevPrimes do: [:x I (n " "x = 0) ifTrue: [ l' false] ].
l' true
77
Afew simple observations will improve the efficiency of this algorithm
and will also illustrate the proper choice of data structures. The loop in
the method for testNumber: haIts and returns as soon as a previous prime
is shown to be a factor of the number under consideration. Two is a factor
of exactly one half of all numbers. Similarly three is a factor of one third
of all numbers, and so on. If we could arrange to test previous primes in
numeric order (that is, in the order in which they were generated) we
would on average remove nonprimes much more quickly than the more
or less random order given to us by a Set. The appropriate data structure
for an ordered collection without keys is a List. Thus we rewrite the al-
gorithm to use a List and the insertion method addLast:, which adds ele-
ments to the end of the list, rather than add:, which would add to the front
of the list. In fact, keeping the previous primes in order allows yet another
improvement in the algorithm since we can terminate the search of pre-
vious primes as soon as a value larger than vn is reached where n is the
value being tested.
Class Primes
I prevPrimes lastPrime I
[
first
prevPrimes ~ List new.
prevPri mes add: (IastPri me ~ 2).
l' lastPrime
next
[ lastPrime ~ lastPrime + 1.
78
\
.';..
The Language
self testNumber: lastPrime l whileFalse.
prevPrimes addLast: lastPrime.
t lastPrime
testNumber: n .
prevPrimes do: [:x I
(x squared> n) ifTrue: [ t true l.
(n "" "" x =0) ifTrue: [ t false l l
An obvious problem with both of these prime number generators is
that they require an ever-increasing amount of storage to maintain the list
of previous prime numbers. If you were constructing a long list of prime
values, the size of this storage could easily become a problem. An alter-
native, which trades slightly longer computation time for reduced storage,
is a recursive generator. This is analogous to a recursive procedure in
programming languages such as Pascal. The following program does not
maintain the list of previous primes but instead regenerates the list each
time a new number is to be tested.
Class Primes
I lastPrime I
[
first
t lastPrime ~ 2
next
[ lastPrime ~ lastPrime + 1.
self testNumber: lastPrime l whileFalse.
t lastPrime
testNumber: n
(Primes new) do: [:x I
(x squared> n) ifTrue: [ t true l.
(n "" "" x = 0) ifTrue: [ i false l l
You may have noted that the message do: is being passed to an instance
of class Primes, which does not contain a method for this message. The
method for do: is inherited from class Object and is defined in terms of
first and next.
do: aBlock I item I
item ~ self fi rst.
[ item notNii l whileTrue:
[aBlock value: item. item ~ self next l.
i nil
"\ \ \ ~ (
;\
,.
.>
Generators 79
Filters
The fact that do: is in class Object and therefore provides functionality
for all objects illustrates the peIVasive nature of generators in Little Small-
talk. Any object can be manipulated as a generator merely by providing
methods for the messages first and next.
An entirely different program can solve the same task as the prime number
generators described in the last section. It uses another programing tech-
nique, filters, that is frequently useful in conjunction with generators. Ex-
temally(that is, examining only the messages to which an object responds)
a filter looks just like a generator. Unlike a "true" generator, however, a
filter does not produce new· values in. response to first or next but takes
values produced by a previously defined generator and modifies them or
filters but values.
The class FactorFilter exemplifies some of the essential features of a
filter. Instances of FactorFilter are initialized by giving them a generator
and a specific nonnegative value. In .response to next (the message first is
in this case replaced by the initialization protocol), values from the un-
derlying generator are requested andretumed, except values for which the
given number is a factor are repressed. Thus the sequence returned by an
instance of FactorFiiter is exactly the same as that given by the underlying
generator, with the exception that values for which the given number is a
factor are filtered out.
Class FactorFilter
I myFactor generator I
[
remove: factorValue from: generatorValue
myFactor ~ factorValue.
generator ~ generatorValue
next I possible I
[ (possible ~ generator next) notNil ]
whileTrue:
[ (possible" " myFactor - =0)
ifTrue: [ t possible]].
i nil
Using FactorFilter, you can construct a simple generator for prime
numbers. First an instance of Interval that will generate all numbers from
2 to some fixed limit is constructed. As each value is removed, a filter is
inserted in front of the generator to insure that all subsequent multiples
) ~
\
'\
\
.'
~ \
80 The l.£mguage
'I-
I
-----------'
of the value will be eliminated. A new value is then requested from the
updated generator.
Class Primes
I primeGenerator lastFactor I
[
first
primeGenerator ~ 2 to: 100.
lastFactor ~ primeGenerator first.
i lastFactor
next
primeGenerator ~ (FactorFilter new;
remove: lastFactor
from: primeGenerator).
i lastFactor ~ primeGenerator next
Pictorially, the underlying generator constructed by the first occur-
rence of the message next can be viewed as follows:
I 1
2 to: n
+---- 2 fi Iter oE-----! generator
When asked for the next prime, the generator is modified by adding a
filter, this time for the last prime value returned, the number 3.
The program continues. Each time a new prime is requested, a filter
is constructed to remove all factors of the previous prime. in this fashion,
all the primes are eventually generated.
~ - i n filter 1+-(--
Of course, like the first two programs in the last section, the storage
required for the chain of filters is proportional to the number of primes
generated so far. Despite this, timings of actual programs show that the
filter program is the fastest of the prime number generating programs
described in this chapter.
-
-
Goal-Directed Evaluation
Generators 81
A useful programming technique when used in conjunction with genera-
tors is goal-directed evaluation. By this technique, a generator is repeatedly
queried for values until some condition is satisfied. In a certain sense the
notion of filters we have just described represents a simple form of goal-
directed evaluation. The goal of instances of FactorFilter, for example, is
to find a value from the underlying generator for which the given number
is not a factor. In the more general case of goal directed evaluation, the
condition frequently involves the outcome of several generators acting
together. An example will illustrate this Il1ethod.
Consider the problem of placing eight queens on a chess board in such
a way that no queen can attack any other queen (Figure 8.3). In this section
we will describe how such a problem can be formulated and solved using
generators, filters, and goal directed evaluation.
We first observe that in any solution, no two queens can occupy the
same column, and that no column can be empty. We can therefore assign
a specific column to each queen at the start, and reduce the problem to
finding a correct row assignment for each of the eight queens.
In general terms, our approach will be to place queens from left to
right (the order in which we assign numbers to columns). An acceptable
solution for column n is one in which no queen in columns 1 through n
can attack any other queen in those columns. Once we have found an
acceptable solution in column 8 we are finished. Before that, however, we
can formulate the problem of finding an acceptable solution in column n
recursively, as follows:
1. Find an acceptable solution for column n - 1. If there is none, return
nil, there is no acceptable solution. Otherwise, place the queen for
column n in row 1. Go to step 2.
Figure 8.3 0 A solution to the eight queens problem
1 234 5 678
1
2
3
4
5
6
7
8
Q
Q
Q
Q
Q
Q
Q
Q
82 The Language
2. Test to see if any queen in columns 1 through n - 1 can attack the
queen in column n. If not, then an acceptable solution has been found.
if the queen can be attacked, then go to step 3.
3. If the queen for column n is in row 8, go to step 4, otherwise advance
the queen by one row and go back to step 2.
4. Find the next acceptable solution for column n - 1. If there is none,
return nil, otherwise, place the queen for column n in row 1 and go
to step 2.
Of course, all positions are acceptable in column 1. Responding to first
corresponds to starting in step 1, whereas responding to next corresponds
to starting in step 3. We represent each queen by a separate object, an
instance of class Queen. Each queen maintains its own position in a pair
of variables and also a pointer to the immediate neighbor on the left. A
skeleton for the class Queen is shown in Figure 8.4. With this skeleton,
our eight queens can be initialized as follows:
lastQueen ~ nil
(1 to: 8) do: [:i IlastQueen ~ Queen new; setColumn: i neighbor:
lastQueen ]
Following the execution of this code the variable lastQueen points to the
last (rightmost) queen.
We have already described our algorithm in terms of finding the first
acceptable position and finding the next acceptable position. It is therefore
easy to apply our generator paradigm (using the messages first and next)
to this situation. Step 1, for example, corresponds to the following method:
first
(neighbor notNil)
ifTrue: [ neighbor first ].
row ~ 1.
i self testPosition
Figure 8.4 0 The class Queen
Class Queen
I row column neighbor I
[
setColumn: aNumber neighbor: aQueen
column ~ aNumber.
neighbor ~ aQueen
\
~ ~
\:.
\
~ 'i,- ~ \ ,:.:
Generators 83
Rather than falling directly into step 2 as we did in the informal description
of the algorithm, an explicit message (testPosition) is used to perform step
2. Before describing the method for this message, we describe the method
used to find the next acceptable position, which is a combination of steps
3 and 4 in our description.
next
(row =8)
ifTrue: [ «neighbor isNil) or: [ neighbor next isNii ])
ifTrue: [ i nil].
row ~ 0].
row ~ row + 1.
i self testPosition
All that remains is to test a position to see if any queen to the left can
attack. As we have already noted, any position is acceptable to the leftmost
queen. Otherwise, we will pass a new message to the neighbor queen asking
if it can attack the position of the current queen. If the neighbor queen
can attack, it will return true; otherwise, it will pass the message on to its
neighbor, and so on, until the leftmost queen is reached. If the leftmost
queen cannot attack, it will return false. Notice the recursive use of the
message next to find the next acceptable position.
testPosition
(neighbor isNil) ifTrue: [ i self].
(neighbor checkRow: row column: column)
ifTrue: [ i self next]
ifFalse: [ i self]
We have reduced the problem to the much simplier one of each queen
taking a pair of coordinates for a queen positioned to the right and re-
sponding whether it or any queen to the left can attack that position. Since
we know the current queen is in a column different from the queen under
test, it can be attacked only if it is in the same row or if the differences in
the columns is equal to the differences in the rows (Le., a diagonal).
checkRow: testRow column: testColumn I column Difference I
columnDifference ~ testColumn - column.
«(row =testRow) or:
[ row + columnDifference = testRow] ) or:
[ row - columnDifference =testRow])
ifTrue: [ i true].
(neighbor notNil)
ifTrue: [ i neighbor checkRow: testRow column: testColumn ]
ifFalse: [ i false]
A final method is useful for producing the answer in a visual form:
\ ;.
\
"\
~ } ~ \
"
84 The Language
printBoard
(neighbor notNil)
ifTrue: [ neighbor printBoard ].
('column " column, ' row', row) print
Putting all the methods for class Queen together, we could type the fol-
lowing example script:
lastQueen ~ nil.
(1 to: 8) do: [:i IlastQueen ~ Queen new; setColumn: i neighbor: lastQueen ]
lastQueen first
lastQueen printBoard
column 1 row 1
column 2 row 5
column 3 row 8
column 4 row 6
column 5 row 3
column 6 row 7
column 7 row 2
column 8 row 4
lastQueen next
lastQueen printBoard
column 1 row 1
column 2 row 6
column 3 row 8
column 4 row 3
column 5 row 7
column 6 row 4
column 7 row 2
column 8 row 5
Operations on Generators
Chapter 3 discussed several messages that could be used in conjunction
with subclasses of Collection to form new collections in various ways. The
message select:, for example, constructed a new collection consisting of
only those elements from the original collection that satisfied some prop-
erty. Thus
(1 to: 10) select: [:x I x '"'" 2 =0]
produced the collection consisting of the even numbers less than 10.
As noted in chapter 3, if all we consider is the message passing interface
there is no way to determine whether an object in response to first and
next is merely looping over values contained in memory, the way most
Generators
\
~ \ - .
85
subclasses of Collection will, or whether it produces values on demand,
such as instances of Interval and the generators described earlier in this
chapter. We can consider both as representing collections (in the case of
the first primes program described, even an infinite collection) of values.
Just as collect: select: and reject: operate on collections to produce new
collections, we would like some method for operating on existing gener-
ators to produce new generators.
Unfortunately, merely 'making our generator a subclass of class Col-
lection will not suffice. The method used in class Collection and similar
subclasses to respond to these messages is to form a new collection, iterate
over the receiving collection gathering the new elements, finally coercing
the new collection, if necessary, into being one of the appropriate class.
Clearly this will not work for infinite generators, such as the prime number
generator. Consider the problem of producing the sequence of values that
are one greater than the prime numbers. We would like to be able to do
something like the following:
primePlusOne ~ (Primes new) collect: [:x I x + 1]
primePlusOne first
3
primePlusOne next
4
In order to do this, it is necessary that the response to messages such as
collect: and select': is to construct a new generator which will, when called
upon, produce the appropriate elements, but will not produce any values
until demanded.
For the purposes of exposition we will divide the operators into two
categories: selection operators, such as select: and reject:, which return
some subset of the values of the original underlying generator; and trans-
formation operators, such as collect:, which modify the values of the un-
derlying generator. Of course, combinations and variations on these ideas
are possible and are explored further in the exercises. In addition, we can
consider ways of combining the sequences from two generators to form a
generator for a new sequence. There are three paradigms we will consider.
Adot-product produces a new sequence by combining the values item-wise
from two sequences; the resulting sequence will be the same length as the
shorter of the two original sequences. A shuffle is an intermixing of the
values from the two sequences; the resulting sequence will be the same
length as the sum of the lengths of the two original sequences. Finally a
cross-product is produced by considering all pairs of values from the two
sequences; the resulting sequence will have a length equal to the product
of the two original lengths. This list is not intended to be comprehensive
but merely to illustrate some of the possibilities.
In order to provide a common functionality to the various forms of
generators we define methods for these messages in a new class, Gener-
86 The Language
ator. We will make Generator a subclass of Collection. Like
instances of Generator will not by themselves be useful. However, when
classes such as the Primes class of an earlier section are made into sub-
classes of Generator, the full functionality of generators can be obtained
through inheritance. To simplify the production of examples in this section,
assume also that the class Interval has been redefined so as to make it a
subclass of Generator.
In response to each of these messages we will produce an object, which
must necessarily be of some class. Let us call the class AbstractGenerator.
Because instances of AbstractGenerator are generators, they must re-
spond to first and next. Their behavior in response to these messages,
however, will vary in different circumstances. One general way of providing
a wide degree of flexibility in behavior is to have the actions of
AbstractGenerator be given by a pair of blocks. Thus the class and meth-
ods for AbstractGenerator can be simple (Figure 8.5), but, as we shall
see, extremely flexible.
Consider first the problem of transforming the values returned by a
given generator. A prose description of the operation of collect: might be
HProduce a value from the underlying generator. If the value is nil, return
nil; otherwise, return the value transformed as indicated by the argument
block." This can be rendered into Smalltalk almost directly:
collect: collectBlock ItestBlock I
testBlock [:x I (x notNil) ifTrue: [ collectBlock value: x ] ].
j AbstractGenerator new;
firstBlock: [testBlock value: self first]
nextBlock: [testBlock value: self next]
Figure 8.5 0 The cfass AbstractGenerator
Class AbstractGenerator: Generator
I firstBlock nextBlock I
[
firstBlock: blockOne nextBlock: blockTwo
firstBlock blockOne.
nextBlock blockTwo
first
j firstBlock value
next
j nextBlock value
"
\
"\ ~ . ~
,. ,.
Generators 87
The temporary variable testBlock is in this case used to factor out the
commonality between the actions taken in response to first and those taken
in response to next; Used in this manner, the block is in many ways similar
to an in-line procedure, with the ability to take parameters (the argument
list) and the ability to be invoked from several locations. Notice that be-
cause blocks must be evaluated in the context in which they are defined,
the "temporary" variable testBlock is not released when the method ter-
minates but must exist for as long as the abstract generator created in the
method exists.
To illustrate the creation of a generator that produces a subset of the
elements from the underlying generator, we present the method for reject:.
The protocol for other methods is similar, and some are explored in more
detail in the exercises at the end of this chapter. In general terms, the
method for reject: must loop over values produced by the underlying gen-
erator until either the base generator becomes exhausted or until a value
failing to satisfy the rejection criteria is encountered. Again, as in the case
of collect:, a block is used to factor out the common behavior in the two
messages. Note that the value of a block is always the value of the last
expression .computed in the block; thus the solitary variable result in
testBlock serves to indicate the value to be returned by the block.
reject: selectBlock ItestBlock result I
testBlock ~ [:x I result ~ x.
[ (result notNiI) and: [ (select Block value: result) I I
whileTrue: [ result ~ self next I.
result I.
t AbstractGemerator new;
firstBlock: [testBlock value: self first I
nextBlock: [ testBlock value: self next I
Using reject:, we can form the set of values from one sequence that are
not elements in a second sequence as follows:
a ~ 1 to: 10
b ~ 3 to: 15 by: 2
c ~ a reject: [:x I b includes: x I
c print
AbstractGenerator ( 1 2 468 10)
Using the companion message, select:, we can generate the intersection
of two sequences:
d ~ a select: [:x I b includes: x I
d print
AbstractGenerator ( 3 5 7 9 )
88
\
The Language
Now consider the problem of combining two generated sequences to
form a new generator. The simplest form is the dot product, which takes
a pair of values, one from each generator, and produces a new value
depending upon this pair. The new generator halts when one or both of
the underlying generators becomes exhausted. The method is very similar
to that used for collect:.
dot: buildBlock with: secondGenerator I y testBlock I
testBlock ~ [:x :y I «x notNil) and: [y notNil ])
ifTrue: [ buildBlock value: x value: y ] ].
1 AbstractGenerator new;
firstBlock: [testBlock value: self first
value: secondGenerator first]
nextBlock: [testBlock value: self next
value: secondGenerator next]
For example the dot product can be used to produce a generator which
represents the maximum values of two sequences.
a ~ 1 to: 9 by: 4
b ~ 2 to: 8 by: 2
a dot: [:x :y I x max: y ] with: b
AbstractGenerator ( 2 5 9 )
The problem of producing a shuffle is considerably more difficult, al-
though intuitively the concept is simple. Consider a message of the fol-
lowing form:
first-generator shuffle: [:x :y I some expression] with: second-generator
At each step a pair of values, one from each of the generators, is evaluated
using the shuffle block. If the block returns a false value, the value returned
is that of the first generator, which is then advanced. If the result of the
shuffle block is true the value returned is that of the second generator. When
either generator becomes exhausted, the remaining values from the other
generator are returned.
For example, a shuffle placing values into numerical order could be
written as follows:
a ~ 1 to: 9 by: 4
b ~ 2 to: 6 by: 2
a shuffle: [:x :y I x > y] with: b
AbstractGenerator (1 2 4 5 6 9 )
A perfect shuffle can be formed by using a counter.
'.
\ ~
\
\
;\
".
Generators
a ~ 1 to: 3
b ~ 10 to: 12
i ~ O .
a shuffle: [:x :y I (i ~ i + 1) " " 2 =0] with: b
AbstractGenerator (1 10 2 11 3 12 )
Placing a constant in the shuffle block will result in a catenation.
a shuffle: [:x :y I true] with: b
AbstractGenerator ( 10 11 12 1 2 3 )
a shuffle: [:x :y I false] with: b
AbstractGenerator ( 1 2 3 10 11 12)
89
Since at each step a comparison must be made between the next values
from each of the underlying generators, it is clear that the method for
shuffle must buffer at least one value from each. Let the variables nextx and
nexty be the next values from the first and second generators, respectively;
then the major portion of the shuffle algorithm can be given as follows:
if nextx is nil
then if nexty is nil
then return nil
else return nexty and advance the second generator
else if nexty is not nil and the comparison is false
then return nexty and advance the second generator
else return nextx and advance the first generator
Adding the code to initialize the buffer variables, this is rendered in
Smalltalk as follows:
shuffle: shuffleBlock with: secondGenerator I nextx nexty result testBlockl
testBlock ~ [(nextx isNil)
ifTrue: [(nexty isNil)
ifTrue: [ result ~ nil ]
ifFalse: [ result ~ nexty. nexty ~ secondGenerator next] ]
ifFalse: [«nexty notNiI) and:
[ shuffleBlock value: nextx value: nexty] )
ifTrue: [ result ~ nexty. nexty ~ secondGenerator next]
ifFalse: [ result ~ nextx. nextx ~ self next] ].
result ].
t AbstractGenerator new;
fi rstBlock: [ nextx ~ self fi rst.
nexty ~ secondGenerator fi rst.
testBlock value]
nextBlock: testBlock
\ "\
\
"\
,
.'
"
90 The Language
The catenate form of shuffle occurs often enough to deserve a method
of its own.
, secondGenerator
t self shuffle: [:x :y I false] with: secondGenerator
We earlier saw how to produce a partial9ifference of two sequences.
By combining this with a catenation, we can form a nonrepeating union
of two generators.
a ~ 1 to: 10
b ~ 3 to: 15 by: 2
c ~ a , ( a reject: [:x I b includes: x ])
c print
AbstractGenerator ( 1 2 3 4 5 6 7 8 9 10 11 13 15 )
Preliminary to a method for forming the cross-product of two gener-
ators, consider a slightly simplier problem: using each value from an un-
derlying generator as a base, construct a new intermediate generator; the
desired sequence is formed by the catenation of the values produced by
the intermediate generators. Here is an example:
a ~ 10 to: 30 by: 10
b ~ a atEachGenerate: [:x I (x + 1) to: (x + 3) ]
b print
AbstractGenerator ( 11 12 13 21 22 23 31 32 33 )
The generator a by itself produces the three-element sequence 10 20 30.
Each of these values is passed in turn to the argument block, which re-
sponds with a new three element generator. By catenating together the
values from each of these generators, a resulting nine element sequence
is produced.
The method for atEachGenerate: incorporates by far the most general
and flexible use of blocks shown in this chapter and illustrates once more
the procedure-like nature of blocks. Two rec4fsive blocks are used to con-
struct each new value. The block buildGen takes a value from the under-
lying sequence and constructs an intermediate generator from it, returning
the first value from this generator. The block testBlock examines a value
from an intermediate generator and either returns it (if it is not nil) or
produces the next intermediate generator. Note the difference between the
block used in the AbstractGenerator to construct the first element and
that used to construct each successive element.
atEachGenerate: genBlock I generator buildGen testBlock I
buildGen ~ [:x I (x notNil)
ifTrue: [ generator ~ genBlock value: x.
testBlock value: generator first] ].
\
).
\
~ ~
> ..~ / ~
Generators 91
testBlock ~ [ :x I (x notNil) ifTrue: [x ]
ifFalse: [ buildGen value: self next] ].
i AbstractGenerator new;
firstBlock: [ buildGen value: self first]
nextBlock: [testBlock value: generator next]
Once the method for atEachGenerate: is defined, it is a simple matter
to combine this with a collect: to produce the cross product.
cross: crossBlock with: secondGenerator
i self atEachGenerate:
[:x I secondGenerate collect:
[:y I crossBlock value: x value: y ] ]
The following illustrates the type of sequence produced by the method
for cross:with:.
a ~ 1 to: 4
b ~ 10 to: 12
a cross: [:x :y I x @ y ] with: b
AbstractGenerator ( 1@10 1@11 1@12 2@10 2@11 2@12 3@10 3@11
3@12 4@10 4@11 4@12 )
Further Reading
In the basic form of an expression that can be repeatedly activated to
provide a succession of different values, the concept of generators appears
in a number of different languages, notably Alphard (Shaw 81), CLU (Lis-
kov 81), MLISP (Smith 80), and Icon (Griswold 83). In most of these
languages, however, the use of generators is very restricted. For example,
in Alphard and CLUJ generators can be used to iterate over the elements
of a programmer-defined data structure but are accessible only in the
context of a particular type of for statement. The use of backtracking and
goal directed evaluation in conjunction with generators was introduced
by the language Icon, a descendent of SNOBOL4 (Griswold 71) and SL5
(Hanson 78). The language Cg (Budd 82) was an attempt to add Icon style
generators to the programming language C. However in Cg, and partially
in Icon, generators are still restricted to a specific expression in a specific
location. The notion of associating the sequence of values produced by a
generator with a named variable or an object, and not with a specific
occurrence of an expression, is achieved in Icon by a related type of object
called a co-expression. Like generators in Smalltalk, co-expressions are
not limited to a single specific expression in a program but carry with
92
The Language
them memory of which values have been already produced and can return
portions of their sequence in different places in a program.
It is, of course, possible to view generators both as objects producing
a succession of values and as an embodiment of an actual (perhaps even
infinite) sequence. The experimental language Seque (Griswold 85) is an
attempt to deal directly with sequences as abstract mathematical objects
rather than as data structures.
In the Smalltalk-80 language (Goldberg 83), the concept of streams is
in many ways similar to the idea of generators. For the most part, streams
use a slightly different interface, namely the pair of messages reset (which
initializes the generator but does not return any value) and next (which is
used for both the first and all succeeding elements). The message do: is
adapted from the streams of (Goldberg 83). An article by Deutsch (Byte
81) discusses in more detail many aspects of generators in the Smalltalk-
80 system.
EXERCISES
1. Rewrite the primes program from the sectionon filters to use instances
of AbstractGenerator in place of FilterFactor.
2. Consider the three variables formed in the following manner:
a <E- 1 to: 3
b <E- AbstractGenerator new; firstBlock: [ a ] nextBlock: [ nil]
c <E- AbstractGenerator new; firstBlock: [ a first] nextBlock: [ a
next]
Discuss the similarities and differences among a, band c. In particular
how does each of them react to the messages first, next and do:?
, 3. Look at the method inherited for select: and reject: in class Collection.
Explain why it is necessary to implement only one of these messages
in class Generator.
4. An alternative to the first next paradigm described in this chapter is
the following pair of messages:
reset Reset the generator to the start of the sequence, but do not
produce a value.
next Return the next value from the sequence (which may be the
first value if the last message sent to the receiver was reset).
Similarly, instead of returning nil to indicate the end of sequence, a
message atEnd could be implemented by each generator. This message
would return true if no more elements could be returned by the gen-
erator, and false otherwise. Discuss the advantages and disadvantages
Generators
\
.".:}
93
of these techniques. Support your opinions by rewriting some of the
generators described in this chapter so that they use these alternatives.
5. Explain why the following does not produce the expected result:
a ~ 1 to 9
b ~ a shuffle: [:x :y I true] with: a
Is this a problem inherent with the generators technique described in
this chapter? How might it be overcome?
6. Why does the following not succeed in producing a sequence of length
4?
i ~ (Primes new) select: [:x I x < 10 ]
Show how to implement methods for the following messages:
while: The argument must be a one-parameter block returning a
boolean value. Return values from the underlying generator
as long as the block evaluates to true. Terminate when the
underlying generator is exhausted or when a value is found
for which the block is not true.
until: Like while: The argument must be one-parameter block re-
turning a boolean value. Produce values from the underlying
generator until the block returns true, or until the generator
is exhausted.
Is it necessary to implement both of these in terms of Abstract-
Generator? Show how, if a method exists for either one, the other can
be implemented in terms of it.
7. The following method in class Generator produces a sequence con-
sisting of the first n elements of the underlying generator, where n is
the integer argument.
first: limit I counter testBlock I
testBlock ~ [:x I counter ~ counter + 1.
(counter> limit)
ifFalse: [ x ] ].
t AbstractGenerator new;
firstBlock: [ counter ~ O.
testBlock value: self first]
nextBlock: [testBlock value: self next]
Show how to implement a method for the message from:to:, that can
be used to extract any contiguous set of values from the underlying
generator, specified in terms of indices. Extend this to return any set
of values for which the indices form an arithmetic progression.
8. Let us say a generated sequence is enumerable if every value in the
94 The Language
sequence will eventually be produced, assuming the generator is quer-
ied long enough;- Consider the message atEachGenerate:; under which
of the following conditions is the resulting sequence enumerable?
a) The underlying generator is finite; each of the intermediate
generators is finite.
b) The underlying generator is finite; however, some of the inter-
mediate generators are infinite.
c) The underlying generator is infinite; however, all of the inter-
mediate generators are finite.
d) The underlying generator is infinite, and some of the inter-
mediate generators are also infinite.
9. Consider the following scheme for assigning indices to the values pro-
duced as a result of the message atEachGenerate:. Let (IJ 1) be the index
of the first value produced by the first intermediate generator; (I,2),
the index of the second value produced by the first generator; (2,1),
the first value produced by the second generator, and so on. If the first
intermediate generator produces n values and the second intermediate
generator m values, then the sequence of indices produced can be
represented as follows:
(1,1) (1,2) ... (1,n) (2,1) (2,2) ... (2,m) (3,1) ...
Construct a method which produces the same values but in the fol-
lowing dovetailed sequence:
(1,1) (1,2) (2,1) (1,3) (2,2) (3,1) (1,4) (2,3) (3,2) (4,1) ...
Note that this involves keeping a list of intermediate generators, rather
than just the single intermediate generator required for atEach-
Generate:. Under which of the conditions given in problem 6 is the
resulting sequence enumerable?
1\
..
%
*

f\


*

*
l\
:8)
*
f\

A :<.
1\
(21

*
i-
/\

*
1\
'#: *
/\

*

>1-
/\
!0

*

/\
(8) *
* /\

'#:
*
/\
::ti-
*

* !\
-

*
/\
r;::...........

*

/\ :,8'I

*>}
/\
(§)

*
f\
*

<
<:«
.'. .c-
< <
'" '-
»:>:::.. >-;»»:»
//////////
{{{{{{{{{{{{{{{{{{{{{{{{{i
-}}}}}}}}}}}}}}}}}}}}}}}}}
««{«««({«««««i
»»»»»
//// / / / / / /
'""'" "'" ',,-"'" "'""'''''' '"'"
'.{{{{{{{{{{{{{{{{{{{{{{f{{
H}}}}}}}}}}}}}}H}}}}
({({«({«««««««(i
))))))))))))))))))))))\
CHAPTER
9
Graphics
95
96 The Language
The Smalltalk language was originally conceived as part of an ambitious
project to design a Dynabook. The Dynabook project grew out of ideas
developed in the late 1960s.by Alan Kay. Apremise of Kay's work was that
eventually it would be possible to place a computer with power equal to
machines that occupied entire rooms into a portable box about the size
of a notebook. In the early 1970s, Kay went to the Xerox Palo Alto Research
Center (Xerox PARC) and there formed the Learning Research Group. As
a first step toward the DYllabook, a goal of the LRG was to develop a
programming environment that would be useful and accessible to nonspe-
cialists, particularly children. (One experiment involved teaching the
Smalltalk-72 language to a group of children ranging in ages from six to
fifteen. 1) That project eventually developed the Smalltalk-80 programming
environment.
The Dynabook was conceived as incorporating a page-sized, high res-
olution, bit-mapped display and a pointing device that could be used to
reference and manipulate images on the screen. In its original idealization,
the screen itself was sensitive to the user's touch, and thus a portion of
the screen could double as a keyboard. Actual ftinterim Dynabooks" de-
veloped at PARC kept the bit-mapped display but replaced the touch screen
with a separate keyboard and a pointing device called a mouse. In the
Smalltalk-80 system, the bit-mapped display and the mouse are repre-
sented as an intrinsic part of the programming environment.
Unfortunately, for many computer users, access to high resolution bit-
mapped displays still represents an unfilled aspiration rather than an every-
day experience. One of the first motives in developing the Little Smalltalk
system was a desire to provide for a large number of people who might
otherwise not be exposed to Smalltalk. To achieve this, it was necessary
to design a system that could execute under a conventional operating
system on conventional processors using nothing more than conventional
character-oriented terminals. Thus the Little Smalltalk project represented
a much less revolutionary and ambitious project than the Smalltalk-80
programming environment, but, because of its nature, a slightly more
accessible one.
1. See "Microelectronics and the Personal Computer" (Kay 77) for a description of this
project. The following quote is notable: .
"After observing this project we came to realize that many of the problems involved in
the design of the personal computer, particularly those having to do with expressive com-
munication, were brought strongly into focus when children down to the age of six were
seriously considered as users. We also realized that children require more computer power
than an adult is willing to settle for in a time-sharing system. The best outputs that time-
sharing can provide are crude green-tinted line drawings and square-wave musical tones.
Children, however, are used to finger paints, color television and sterophonic records, and
they usually find the things that can be accomplished with a low-capacity time-sharing system
insufficiently stimulating to maintain their interest."
\. \
\ ':.
~ '.0: ...}
.,
~ \
Graphics 97
Despite the lack of emphasis on graphics in the Little Smalltalk system,
it is still possible to produce some graphics functions using Little Smalltalk.
The type of graphics capabilities attainable depends on the nature of the
devices on which the Little Smalltalk system is executed. For this reason
this chapter is divided into three parts. The first part describes routines
for simple character graphics. Character graphics require nothing more
than conventional terminals but are therefore severely constrained in the
nature and quality of the results. The second part of this chapter, line
graphics, discribes routines for devices such as the Tektronix 4014 ter-
minal, which can display straight lines, circles, and other line forms. The
final part, bit-mapped graphics, develops techniques that can be used with
devices which permit the user to set the value of individual pixels (dots).
It is assumed that most users will have access to devices for which the
first forms are possible, many will find the second level accessible, and a
few will be able to experiment with the third form of graphics.
Character Graphics
The primitive operation upon which all our character graphics will be
constructed is moving the cursor to a specific location on the screen (usu-
ally expressed by a Point representing an x-y coordinate pair) and at that
location, printing a string. Unfortunately, as with almost all attempts at
device-independent graphics, even such a simple operation as this is
thwarted by the great number and diversity of terminals in existence. Each
terminal manufacturer seems to use a different protocol for screen oper-
ations.
One attempt to circumvent this problem is the termlib/curses facility.2
The name termlib is used to describe a database of terminal descriptions.
The curses terminal output package consults this database, finding the
entry which matches the terminal the user is running on. From the de-
scription given there, it determines the correct sequence of operations
necessary to perform simple terminal output, such as clearing the screen,
backspacing, inserting characters, or moving the cursor to a specific lo-
cation and printing a string.
2. The curses screen package is described in "Screen Updating and Cursor Movement
Optimization: A Library Package" by Kenneth C. R. C. Arnold. This document is usually
distributed along with the Berkeley 4.2 (and subsequent) version of Unix. It is also available
under many versions of Unix that advertise "Berkeley enhancements." The curses package
actually provides many more capabilities than are used here, such as the ability to do win-
dowing. The interested reader may wish to investigate how these can be incorporated into
the Little Smalltalk system. (See the section on windowing in the Projects chapter.)
"-
1"-
1"- 1/ II.
[-- * /1 0
1

<--------/ 1-----1
1

98 The Language
The primitive operations necessary to support character graphics are
hnplemented using calls on the curses output package. You can discover
whether your system has been configured to use the package by sending
tpe message printAt: to a string, for example
'hello worldI printAt: 10 @ 10
If the result is printed at the specified location, the curses routines are
operational on your system.
The primitive units with which character graphics are defined are
forms, arid in particular, so as to distinguish them from the forms we will
discuss in subsequent sections, instances of the class CharacterFonn. A
form represents some printable image. We can categorize the operations
we will provide for forms into four groups: definition operations which
describe the basic nature of the form; transformation operations which
metamorphose the form into a different, but related, form; combination
operations which combine one form with another; and display operations
which print a form on the output.
Internally, the class C4aracterFonn maintains an array of strings,
representing the textfor the form (Figure 9.1). The size of this array rep-
resents the height of the form and the .maximum size of any string the
width. Elements in the text array are defined row by row, using the message
row:put:. For example, the following sequence:
plane <E- CharacterForm new.
plane row: 1 put: I
plane row: 2 put: I
plane row: 3 put: 1
plane row: 4 put: I
plane row: 5 put: I
plane row: 6 put: I
plane row: 7 put: 1
would produce a form representing an airplane, which displays as follows:
"-
1"- ----------
["-"-'--------1 /---1
1 -- * 1 1 0
<--------1 /-----[
Graphics
Figure 9.1 0 The class Character Form
Class CharacterForm
I text I
[
new
text ~ Array new: 0
columns
t text inject: 0 into: [ :x :y I x max: y size]
extent
t self rows @ self columns
row: index
t text at: index ifAbsent: [ I I ]
row: index put: aString
(index> text size)
ifTrue: [ [ text size < index] whileTrue:
[text ~ text grow: 1 I ] ].
text at: index put: aString
rows
t text size
99
Because of the ability to print a string at a specific location on the
teIminal, instances of CharacterForm can also be displayed at a specific
location using the following method:
printAt: aPoint I location I
location ~ aPoint copy.
text do: [ :x I x printAt: location.
location x: (location x + 1)]
Note the spaces surrounding the characters in our plane example. By
using the fact that these spaces will overprint earlier versions, we can use
this example to produce a simple type of animation. For example the
following will cause the plane to move to the right and down, starting in
the upper left hand comer of the screen:
(1 to: 9) do: [ :i I plane printAt: i @ (3 * i) ]
Reversals, rotations and clipping are among the transfoImations to
100
\
,'.0:.
The Language
modify a form. A reversal is merely a mirror image of a form and can be
constructed using the following method:
reversed rnewForm columns NewRow 1
columns ~ self columns.
newForm ~ CharacterForm new.
(1 to: self rows) do: [ :i 1
newRow ~ (text at: i) padTo: columns.
newForm row: i put: newRow reversed ].
i newForm
Note that the original form can be Uragged," that is, the rows need not
all be of the same width. To insure that columns line up correctly in the
reversal each row must be padded with spaces to a uniform length. Un-
fortunately, only the strings, and not the individual characters are reversed.
This can cause odd effects if a great number of asymmetric characters are
used. For example, our plane becomes the following when reversed:
"'-
-------- "'-I
1----1 1-------"'- "'-I
o II *-1
1-----/ 1--------<
Rotations have a similar problem, since an image with an equal num-
ber of columns and rows does not display as square. Nevertheless, a ro-
tation can be accomplished using the following method:
rotated 1 newForm rows newRow 1
rows oE- self rows.
newForm ~ CharacterForm new.
(I to: self columns) do: [:i 1
newRow ~ String new: rows.
(I to: rows) do: [ :j 1
newRow at: «rows - j) + 1)
put: «text at: j)
at: i ifAbsent: [ $ ] ) ].
newForm row: i put: newRow].
i newForm
A clipping represents a square subportion of a form, identified by a
pair of points representing the upper left-han"d and lower right-hand cor-
ners of the clipping. For example, the clipping from 2 @ 2 to 5 @ 9 of our
plane example produces the following:
Graphics
""-
1""-
1""- ""----
1-- *
Clippings can be produced using the following method:
c1ipFrom: upperleft to: 10werRight
1 newForm newRow rsize left top rText 1
101
left ~ upperleft y - I. II left hand sidell
top ~ upperleft x - I.
rsize ~ 10werRight y - left.
newForm ~ CharacterForm new.
(upperleft x to: 10werRight x) do: [ :i 1
newRow ~ String new: rsize.
rText ~ self row: i.
(I to: rsize) do: [ :j 1
newRow at: j
put: {rText at: (left + j)
ifAbsent: [$ ] ) ].
newForm row: (i - top) put: newRow].
i newForm
Aform can be placed into another form in one of two ways. An opaque
overlay completely obliterates whatever was originally found in the area
where the form is being placed. The more useful transparent overlay places
only the nonspace characters, allowing whatever was located in the original
to show through. A transparent overlay of one form on another can be
accomplished using the following method.
overLayForm: sourceForm at: startingPoint
1 newRowNumber rowText left rowSize 1
newRowNumber ~ startingPoint x.
left ~ startingPoint y - I.
sourceForm do: [ :sourceRow 1
rowText ~ self row: newRowNumber.
rowSize ~ sourceRow size.
rowText ~ rowText padTo: (left + rowSize).
(I to: rowSize) do: [ :i 1
«sourceRowat: i) ~ = $ )
ifTrue: [ rowText at: (left + i)
put: (sourceRow at: i) ] ].
self row: newRowNumber put: rowText.
newRowNumber ~ newRowNumber + I]
102 The Language
Slightly better "animation" can be produced using transparent overlay.
Suppose, for example we have defined a pair of forms, one our plane
and the second a cloud. Each flframe" of the animation can be displayed
by creating a new form, placing the cloud at the appropriate location
and then transparently laying the plane over the cloud. With the trans-
parent overlay, the plane will appear to be in front of the cloud, but
the cloud will still appear through any blank spaces in the plane form. As
each frame is constructed it is printed and work is begun on the next
frame.
The exercises at the end of this chapter suggest several other uses for
character graphics.
Line Graphics
Some terminals, such as the Tektronix 4014, can draw a limited range of
figures, such as straight lines or circles. AUnix interface has been provided
to these. through the plot(3) routines.
3
In character graphics the funda-
mental unit was the CharacterForm. In line graphics the fundamental
object is the Pen.
4
Conceptually, a Pen can be thought of as a writing
instrument for the terminal screen. Four quantities describe the state of
the Pen. The first two are a pair of coordinates, describing the current
location of the pen. Using the message exte11t:to:, the user can control the
range of legal values for pen coordinates. The third quantity is a direction,
expressed as a value between 0 (straight up) and 27T (also straight up). The
final quantity is a binary state which is either up, in which case pen motions
do not produce any output, or down, in which case the pen produces a
line as it is dragged across the terminal screen. The interface to the class
Pen is shown in Figure 9.2.
Bya sequence of simple directives, pens can be used to draw various
shapes. For example, the following expression:
3. Note that unlike the curses routines, which determine the terminal type at run time,
the plot routines use a different C library for each terminal type. Thus the terminal type is
compiled as part of the executable file. For this reason there may be a different version of
sf created for each device supporting the plot(3) functions. Also note that some other terminal
types, such as the HP 2048, may have a C interface permitting terminal manipulation that
is different from the plot(3) interface. To use these terminals may require modifying the
primitive handler. (See Chapters 11-15.)
4. The class Pen is adapted from the Smalltalk-80 graphics kernel, which in turn was
inspired by "Turtle Geometry" in the language LOGO (Abelson 81).
\
.\
Graphics
Figure 9.2 D Interface to the classPen
103
message function
ci rcieRadius: Draw a circle at the current location with a radius given by the
qrgument.
direction Return the current direction of the pen (a number between 0 and 21T).
direction: Set the pen to point in the direction given by the argument.
down Set the pen to down (movement will cause output).
erase Erase the terminal screen.
extent: to: Establish the coordinate range for the pen. The arguments represent the
lower left-hand upper right-hand respectively.
go: Move in the current direction by an amount given in the argument.
goTo: Move to the location given by the argument, must be a Point.
isUp Return true if the pen is curre!1t1y up, false otherwise.
location the cyrrent location of the pen as a Point.
turn: Turn the direction of the pen clockwise by the amount given in the argument.
up Set the state of pen to up. When up, movement will not cause output.
4 timesRepeat: [ aPen go: 10 ; turn: 0.5 pi ]
draws a box:
D
We can try this with various numbers of sides to draw a sequence of regular
polygons: -
104 The Language
(3 to: 6) do: [ :nsides Insides timesRepeat: [ aPen go: 10 ; turn: ( 2 pi Insides) ] ]
Producing the following picture:
A pen permits one to draw directly on a screen. In order to preserve
and manipulate images, it is necessary to store the information about a
given image. Afirst step is to define a class representing an individual line.
Instances of class Line (Figure 9.3) maintain a starting and ending point
and can be instructed to display themselves using a specified pen. In ad-
dition, a new Line can be created offset from the original.
Figure 9.3 0 The class Line
Class Line
I startPoint endPoint I
[
from: sPoint to: ePoint
startPoint <E- sPoint.
endPoint <E- ePoint
+ offset
t Line new; from: (startPoint + offset)
to: (endPoint + offset)
drawWith: APen
aPen up.
aPen goTo: startPoint.
aPen down.
aPen goTo: endPoint
Figure 9.4 D The Class LineForm
Class LineForm
I lines I
[
new
lines <Eo- Bag new
Graphics
\
105
add: startingPoint to: endingPoint
lines add: ( Line new; from: startingPoint to: endingPoint )
with: aPen displayAt: location
lines do: [ :aLine I
(aline + location) drawWith: aPen 1
A LineForm (Figure 9.4) consists of a collection of Lines. New lines
are added to the collection by means of the message add:to:. Like
CharacterForms, instances of LineForm, can be instructed to, display
themselves at a specific location on the terminal screen. Also, as with the
character case, methods to produce various transformations (such as re-
versals, rotations by an arbitrary amount, or clipping) and combinations
of line forms can 'be constructed.
The class LineForm is useful by itself if the starting and ending points
are easy to compute. If a figure is produced as part of a computation using
a Pen, however, it would be inconvenient to rewrite the computation
merely to save the endpoints of the lines generated. A better solution is to
use a subclass of Pen, called Stencil (Figure 9.5). AStencil is like a Pen,
but, instead of actually writing on the terminal screen, it saves the lines
it would have generated in a form. Because of inheritance, it is necessary
only to redefine the single message goTo:. Note carefully the use of self
and super to insure the messages are matched to the correct methods.
Using stencils and forms, we could make three copies of our polygon
picture as follows:
aForm <Eo- LineForm new
aPen <Eo- Stencil new; setForm: aForm
(3 to: 6) do: [ :nsides Insides timesRepeat:
[ go: 10 .aPen turn: (2 pi / nsides ) ] ]
penTwo <Eo- Pen new
aForm with: penTwo displayAt: 0 @ 0
aForm with: penTwo displayAt: 10 @ 10
aForm with: penTwo displayAt: 0 @ 20
Producing the following picture:
\
). -
i 06 The Language
Figure 9.5 0 The Class Stencil
Class Stencil: Pen
1saveForm I
[
setForm: aForm
saveForm ~ aForm
I
goTo: APoi nt
(self isUp)
ifTrue: [ super goTo: aPoint ]
ifFalse: [ saveForm add: self location to: aPoint.
self up.
super goTo: aPoint
self down]
Bit-Mapped Graphics
The most general type of graphics requires the ability to turn on and off
individual pixels (dots) on the terminal screen. A device that can do this
is often called a "Bit-Mapped Display." Unfortunately, such devices are
Graphics
Figure 9.6 0 A digital form representing t h ~ word Budd
• • • • •
• • • •
• • • •
• • • • • • • • • • •
• • • • • • • •
• • • • • • • •
• • • • • • • • • •
107
still relatively new and their interface completely unstandardized. There-
fore the concepts of bit-mapped graphics can be discussed only in rough
general terms, leaving the specific details for the user to work out.
Abstractly, a digital (bit-map) form can be thought of as a two-dimen-
sional array of bits, representing either dots or spaces. Figure 9.6 illustrates
a digital form representing the word "Budd. J I A two-Dimensional form
possesses a height (7 in this case) and a width (19).
Internally, this form can be represented in a number of ways. For
example, the height and width could be maintained by a Point ( 7 @ 19)
and the bit values by a ByteArray ( #[ 224 4 50 0 134 64 16 242 94 121
74 120 231 56 ], for example, if we group bits by eight and read from top
to bottom, left to right). Primitive operations would have to be defined to
turn on and off a specified set of pixels on the terminal from this repre-
sentation.
Bit-mapped graphics are very powerful since they can be used to rep-
resent anything that can be displayed on the screen. For example, the class
Pen, described in the last section, can be constructed out of bit operations.
5
The only message which would require changing to support pens is the
drawing produced by the message goTo:. We here describe only a portion
of the algorithm necessary to support this command. Assume the endpoints
of a line have been already normalized, so we wish to draw a line from
pixel (fx, fy) to (tx, ty). Assume furthermore that fx is to the left of tx. The
following algorithm then draws a rough line one pixel wide.
yf ~ fy asFloat.
ydiff ~ (ty - fy) / (tx - fx).
(fx to: tx)do: [ :x I
set point at x @ (yf rounded)
yf ~ yf + ydiff]
5; The Pen described here is extremely crude. For a more detailed description of a better
pen algorithm, see the description of the graphics kernel (Goldberg 83).
108
. ~
':. \ \
> ~ \ .\ ~ \
The Language
Depending upon the quality of the output device being used, it may
be possible to achieve quite complex effects, such as shading and halfton-
ing. Unlike the Smalltalk-80 system, these effects are only incidental to
Little Smalltalk, so we will not discuss the possibilities here; however, an
excellent description of the techniques used in the Smalltalk-80 graphics
kernel can be found (Goldberg 83
6
).
6. The Smalltalk-80 book gives a detailed description of the infamous BitBIt (for bit
block transfer) operation, which is at the heart of the Smalltalk-80 graphics kernel. As an
indication of the complexity these operations can involve, instances of BitBIt are initialized
with the eight-argument message destForm:sourceForm:halftoneForm:combinationRule:
distOrigin: sourceOrigin:extent: elipRect:
\
'\
>.. "
.:
.~ .
///////////
'""-, '"'-" '"'"',,- '"'""
{{{{{{{{ {{{{{{{{{{{{{{{{{.1
-}}}}}}}}}}}}}}}}}}}}}}}}l
««««{««««««{(,
/ / /// / /// //
"- '"'"'-'" '"'"'-" '"'"-'-,
.f {{ {{ {{{ {{ {{ {{{{{ {{{{{{{{
n}}}}}D}}}}}}}}}}}}}}}}}
({«««((((({((!
i))))))))))))))))))))))
CHAPTER
10
Processes
109
'.
"\ ;.
'\
;\
'>
.,.
110 The Language
The notion of generator, introduced in chapter 8, can be viewed in terms
of a producer-consumer relationship. That is, the generator can abstractly
be considered to be producing some commodity, and the calling routine
considered to be a consumer processing instances of the commodity. In a
system where either the producer or consumer must directly request action
from the other via.a message, there are basically two organizing schemes
one could use: either the generator process is considered to be "in control"
and it calls the consumer process for processing each element, or the
consumer process is considered to be the dominant force, calling the gen-
erator for each new element produced. These two viewpoints are reflected
in the two protocols described in Chapter 8 for dealing with generators.
In the first scenario (Figure 10.1), the consumer is !lin control." When
the consumer requires a value, it initiates the producer using first. Each
time a subsequent value is requested, the producer is queried using next.
Notice that the consumer can follow any desired control flow, but that the
response of the producer is given by the same code and is executed re-
peatedly in response to first and next. Between calls to next, no execution
takes place in the producer.
An alternative organization is shown in Figure 10.2. Here, once control
is given to the producer via the message do:, the producer is "in control."
The producer is free to interpret this message in whatever manner it de-
sires, following whatever control flow is necessary. As each element in the
generated sequence is produced, the block used as argument with the do:
command is executed using the value as argument. Between these uses,
no execution takes place in the consumer.
Which of these two viewpoints is the "correct" way to view the pro-
ducer-consumer relationship will depend upon circumstances. They both
have their advantages. In some instances, however, a third alternative is
preferable. Rather than one process subordinate to the other, they can run
Figure 10.1 0 The producer-consumer relationship, consumer controlling
Producer
producer produces a value
i value
Consumer
consumer requires a value
producer next
consumer is suspended
consumer resumes execution
Processes
Figure 10.2 0 The producer-consumer relationship, producer controlling
111
Producer
producer started using do:
computes a hew value
consumerBlock value: newValue
(producer is suspended)
producer resumes execution
Consumer
consumer is started
consumer executes block
consumer returns
in parallel (Figure 10.3). As the producer generates each value, the values
are placed intb a common container called a mailbox or port. I Each time
the consumer requires a v a l u e ~ it retrieves the object most recently placed
into the mailbox. Aclass description for a sirriple form of mailbox is shown
in Figure 10.4. Note that in this scheme neIther process initiates the other,
and each can execute independently. Because of the symmetry of this
relationship, such processes are called coroutines. As long as: the sequence
of deposits and retrievals meshes correctly, the results will be correct.
If the coroutine technique is to be allowed in Smalltalk, two or more
processes must be permitted to execute in paralIeI.2 In Little Smalltalk the
user can create a new process using the message newProcess and a block.
aProcess ~ [ some actions] newProcess
1. The term port in this context and many of the ideas used in this chapter are due to
L. Peter Deutsch (Byte 81).
2. Of course, on a single processor machine very little ever actually happens physically
in parallel, at least at the program level. The term here refers to logical, not physical, par-
allelism.
\
112 The Language
Figure 10.3 0 The symmetric producer/consumer relationship
\.
Producer
producer produces first element
places element in mailbox
producer starts producing
second element
places second element
in mailbox
starts producing
third element
Consumer
consumer picks first
element from mailbox
consumer processes first element
consumer picks second
element from mailbox
Anewly created process does not immediately begin execution. Instead,
it is said to be in a suspended state. There are four possible states in which
a process can be. They are described as follows:
Active An active process is one that is currently executing. An ac-
tive proc-ess can be temporarily halted by passing it the
message suspend. It can be permanently halted by passing
it the message terminate.
Suspended A suspended process is one that is ready for execution but
is not currently executing. Asuspended process can be made
active by passing it the message resume.
Figure 10.4 0 A simple version of class Mal180x
Class MailBox
I holder I
[
place: anltem
holder ~ anltem
retrieve
t holder
\.
\
\
\
,
~ \
):
Processes 113
Blocked A blocked process is one that is currently on some sema-
phore queue (described below). Blocked processes are un-
blocked by the associated semaphore.
Terminated A terminated process is one that has either finished nor-
mally or been terminated explicitly by a terminate message.
A terminated process cannot be restarted, although the ob-
ject representing it will continue to exist as long as there
are pointers to it.
A newly created process can be directed to commence execution by
passing it the message resume.
aProcess resume
The pseudo variable selfProcess always refers to the currently running
process. A process can commit suicide by passing the message terminate
to selfProcess.
The message state will return a symbol indicating the current state of
a process. The message yield is a no-op (it returns nil) but has the side
effect of passing control to the next process in the queue of active processes.
The process sending the message will be restarted again when it reaches
the front of the queue.
The message fork is a combination of newProcess and resume and can
be used to create unnamed processes. Executing processes run in parallel
with each other, and output from two separate processes may intermix in
arbitrary ways. For example:
[ 5 timesRepeat: [ Iprocess one· print] ] fork
5 timesRepeat: [ ·process two· print]
process one
process two
process two
process one
process one
process one
process two
process one
process two
process two
These are also versions of fork: and newProcess: which permit param-
eters to be passed to arguments in blocks.
[ :num I num timesRepeat: [ Jdid it
'
print] ] forkWith: #( 3 )
did it
did it
did it
'\
•.\
.\ \
5 ~
"
114 The Language
Figure 10.5 0 A problem with the simple mailbox
Producer Consumer
mailbox place: labc
l
mailbox place: Ixyzl
mailbox retrieve
mailbox retrieve
Semaphores
The fact that we cannot predict ahead of time the way in which two
independent processes will interleave illustrates one of the pitfalls of con-
currency. Consider the sequence of events shown in Figure 10.5. Here input
'abc' is placed into the box, and then overridden without being read. Fur-
thermore, input 'xyz' is read twice. The first problem is easy to fix by
maintaining in the mailbox a list of entries rather than just a single item.
The second problem is more difficult.
In order to synchronize the actions of two or more executing processes,
Little Smalltalk defines a new class of objects call Semaphore. A sema-
Figure 10.6 0 An improved mailbox
Class MailBox
I items counter I
[
new
items ~ List new.
counter ~ Semaphore new: 0
place: anltem
items addLast: anltem.
counter signal.
retrieve
counter wait.
t items removeFi rst
Processes 115
Monitors
phore responds to a pair of messages, signal and wait, and can abstractly
be thought of as implementing a non-negative counter and a queue of
pending processes. The message wait attempts to decrement the counter.
If the counter is nonzero, its value is merely updated. If the counter is
zero, the currently running process is suspended. The message signal either
increments the counter, if it already has a nonzero value, or resumes the
first process waiting for the semaphore. A semaphore can be created with
any desired initial value for the counter by passing a number with the
message new:.
In the producer/consumer example, one use for a semaphore is as a
counter measuring the number of items placed in the mailbox. When the
consumer attempts to remove an object from an empty mailbox, it will be
suspended until the producer has had time to produce an entry. Such a
solution is shown in figure 10.6
There is one problem that is not solved by the use of semaphores in Figure
10.6. Suppose the producer and the consumer attempt to access the mail-
box at the same time. Since both methods may attempt to modify the list
containing the mailbox entries simultaneously, the results can be unex-
pectecl. Sections of code for which it is important that only one process
be executing at any time are called critical sections.
The critical section problem can be solved using a binary semaphore.
A binary semaphore is simply a semaphore that takes on the values zero
and one. Such a semaphore can be thought of as encoding a permission
to perform some task. Avalue of one means no process currently has this
permission, and any process that asks for it will be granted. A value of
zero means that some process currently is holding the permission, and
any other process that requests it will be suspended until the process
currently holding the permission relinquishes it. The class Semaphore
provides the following method for implementing critical sections:
critical: aBlock
self wait.
aBlock value.
self signal
If two or more processes attempt to execute blocks established as
critical sections for the same semaphore, all but the first will be suspended,
and each will be restarted in turn as the earlier processes are finished.
Using this facility, our mailbox example can be improved as shown in
Figure 10.7. Here two semaphores are used. The first, as before, is a counter
and is used to suspend the consumer if there are no elements in the items
116
Figure 10.7 D The mailbox as a monitor
Class MailBox
I items counter mutex I
[
The Language
\
new
items +- List new.
counter +- Semaphore new: O.
mutex +- Semaphore new: 1
place: anltem
mutex critical: [ items addLast: anltem ].
counter signal.
retrieve I result I
counter wait.
mutex criticaI: [ resuIt +- items removeLast ].
t result
list. The second semaphore is used to implement a critical section around
the insertion or removal of information from the list. The encapsulation
of a data structure in an object which manages its own critical section
behavior, such as the mailbox example provides for the items list, is known
as a monitor.
Dining Philosophers Problem
To further illustrate the utility of semaphores, we will examine a solution
to the "dining philosophers" problem. The dining philosophers is consid-
ered a classical problem in synchronization, not because of any practical
importance, but because it can be considered a model for a large class of
concurrency control problems.
The problem can be stated as follows (from J. L. Peterson and A.
Silberschatz, Operating System Concepts, (Reading, Massachusetts: Addi-
son-Wesley, 1985), pp. 347-348.):
"Five philosophers spend their lives thinking and eating. The philosophers
share a common circular table surrounded by five chairs, each belonging
to one philosopher. In the center of the table there is a bowl of rice, and
the table is laid with five chopsticks (Figure 10.8). When a philosopher
thinks, he does not interact with his colleagues. From time to time, a
\
.".0:.
Processes
Figure 10.8 0 The table used by the dining philosophers
D
o
117
philosopher gets hungry and tries to pick up the two chopsticks that are
closest to him (the chopsticks that are between him and his left and right
neighbors). A philosopher may only pick up one chopstick at a time. Ob-
viously, he cannot pick up a chopstick that is already in the hand of a
neighbor. When a hungry philosopher has both his chopsticks at the same
time, he eats without releasing his chopsticks. When he is finished eating,
he puts down both of his chopsticks and starts thinking again."
The principal difficulties encountered in a solution to this problem are
twofold. First, we must ensure that no two adjacent philosophers can pick
up the same chopstick at the same time. This problem is easily solved
by representing each chopstick by a (binary) semaphore. A philosopher
endeavors to possess a chopstick by passing the wait message to the as-
sociated semaphore. Similarly, the chopstick is released by passing the
signal message to the semaphore. Thus the life of a philosopher can be
described as follows:
[true] whileTrue:
[ " pick up chopsticks"
leftChopStick wait.
rightChopStick wait.
self eat.
II set down chopsticks II
leftChopStick signal.
rightChopStick signal.
self think.
].
While the use of semaphores guarantees that no two philosophers can
. eat simultaneously, it does not overcome the second problem associated
).
\
\
';.
..'} .>
.'
118 The Language
with this exercise, namely that of deadlock. Imagine that each of the five
philosophers becomes hungry simultaneously. Each will grab his left chop-
stick. Since no philosopher will release either of his chopsticks until he
has eaten, as each philosopher tries to grab his right chopstick he will be
delayed. Thus our philosophers will sit forever, each holding one chopstick
in his left hand waiting patiently for his neighbor to finish eating (although
his neighbor is similarly waiting for his neighbor).
Several possible remedies to the problem of deadlock can be proposed.
For example, Peterson and Silberschatz list the following:
D Allow at most four philosophers to be sitting simultaneously at the
table (so there is always an empty place).
D Allow a philosopher to pick up his chopsticks only if both of them are
available.
D Use an asymmetric solution. That is, an odd philosopher picks up first
his left chopstick and then his right chopstick, while an even philos-
opher picks up his right chopstick and then his left chopstick.
We will use the third solution. Each philosopher is assigned a unique
number, maintained in the variable name. We will also use this value to
print out a message each time the philosopher changes state (for example
going from thinking to eating). If the number is even, the philosopher picks
up his left chopstick first; otherwise, he picks up his right chopstick first.
The methodgetChopSticks describes the selection of both chopsticks, using
the method printState to print out a record of the change of state.
GetChopSticks
self printState: lmoving
l

«name " " 2) = = 0)
ifTrue: [ leftChopStick wait. rightChopStick wait]
ifFalse: [ rightChopStick wait. leftChopStick wait]
printState: state
( IPhilosopher I, name, I is I, state) print
Similarly the method releaseChopSticks implements the transition from
eating to non-eating.
releaseChopSticks
self pri ntState: Ifi nishedI.
leftChopStick signal.
rightChopStick signal
In order to introduce a bit of nondeterminism into the solution, we
include a random variable. Each philosopher eats or thinks for a period
of time randomly determined, represented by the process yielding control
to the next process a random number of iterations. Thus if rand is the
'.
s.
Processes i19
~ ~ w : aNumber
rand +- Random new.
rand randomize.
name +- aNumber
Figure 10.9 0
random variable, the processes of eating and thinking can be represented
as follows:
eat
self printState: leating
l
.
(rand randlnteger: 15) timesRepeat: [selfProcess yield]
think
self printState: lthinking
'
.
(rand randlnteger: 15) timesRepeat: [selfProcess yield]
In order to present a finite solution, we introduce a counter time. This
counter represents the number of times a philosopher will eat in a day.
Thus one day in the life of a philosopher is represented by the following:
time timesRepeat:
[ self think.
self getChopSticks.
self eat.
self releaseChopSticks
].
self sleep.
Putting everything together gives us the class Philosopher shown in
Figure 10.9. The Class DiningPhilosophers (Figure 10.10) can be used to
initialize the chopsticks semaphores and the philosophers appropriately.
The argument to the message new: is the number of philosophers and that
of the message dine: is the number of times the philosopher will eat in a
day. A representative output for five philosophers eating two times a day
is as follows:
The class Philosopher
Class Philosopher
I rand leftChopStick rightChopStick name I
[
leftChopStick: !chop rightChopStick: rchop
leftChopStick +- Ichop.
rightChopStick +- rchop.
Program Continued
120
\.
The Language
getChopSticks
self printState: Imoving
'
.
((name" " 2) = = 0)
ifTrue: [ leftchopStick wait. rightChopStick wait]
ifFalse: [rightChopStick wait. leftChopStick wait]
printState: state
ePhilosopher I, name, I is I, state) print
releaseChopSticks
self printState: 'finished'.
leftChopStick signal.
rightChopStick signal.
think
self printState: 'thinking
'
(rand randlnteger: 15) timesRepeat: [selfProcess yield]
eat
self printState: 'eating
'
.
(rand randlnteger: 15) timesRepeat: [selfProcess yield]
philosophize: time
[ time timesRepeat:
[ self think.
self getChopSticks.
self eat.
self releaseChopSticks
].
self printState: 'sleeping!
] fork
Philosopher 1 is thinking.
Philosopher 2 is thinking.
Philosopher 3 is thinking.
Philosopher 4 is thinking.
Philosopher 1 is eating.
Philosopher 5 is thinking.
Philosopher 3 is eating.
\
.."""=.
\
...~ , .
Philosopher 5 is eating.
Philosopher 2 is eating.
Philosopher 4 is eating.
Philosopher 1 is thinking.
Philosopher 2 is thinking.
Philosopher 3 is thinking.
Philosopher 4 is thinking.
Philosopher 1 is eating.
Philosopher 5 is thinking.
Philosopher 3 is eating.
Philosopher 5 is eating.
Philosopher 2 is eating.
Philosopher 4 is eating.
Philosopher 1 is sleeping.
Philosopher 2 is sleeping.
Philosopher 3 is sleeping.
Philosopher 4 is sleeping. \
Philosopher 5 is sleeping.
Processes
"\
...,>, .
121
Figure 10.10 0 The class DiningPhilosophers
Class DiningPhi losophers
I numberDiners chopSticks philosophers I
[
new: aNumber
numberDiners ~ aNumber.
chopSticks ~ Array new: numberDiners.
philosophers ~ Array new: numberDiners.
(1 to: numberDiners) do:
[ :p I chopSticks at: p put: (Semaphore new: 1).
philosophers at: p put: (Philosopher new: p) ].
(1 to: numberDiners) do:
[ :p I (philosophers at:
leftChopStick: (chopSticks at:p)
rightChopStick: (chopSticks at: «p " " numberDiners) + 1)]
dine: time
(1 to: number Diners) do:
[ :p I (philosophers at: p) philosophize: time]
122
\
.. ~ -
The Language
Further Reading
Many of the concepts discussed in this chapter, for example the notion of
mailboxes, are adapted from a paper by L. Peter Deutsch in the special
issue of Byte devoted to Smalltalk (Byte 81).
The dining philosophers problem was originally stated and solved
by Dijkstra (Dijkstra 65). It is discussed in most operating systems text-
books. The version used here is taken from Peterson and Silberschatz
(Peterson 83).
EXERCISES
1. Explain why the method for place: in Figure 10.7 could not be written
as follows:
place: anltem
counter signal.
mutex critical: [ items addLast: anltem ]
2. Explain why the method for retrieve in Figure 10.7 could not be written
as follows:
retrieve
counter wait.
mutex critical: [ t items removeLast ]
3. Write a solution to the Dining Philosophers problem in which each
philosopher is allowed to pick up his chopsticks only if both of them
are available. Note that the easiest way to do this would be to introduce
a monitor for the chopsticks semaphores, and modify the values of the
chopsticks array only in a critical section.
4. How might processes be used to provide an alternative method for
doing simulations, such as those described in Chapter 7? Produce a
simulation for the Ice Cream store of Chapter 7 using processes.
_.-
\.
\
.'l:.
"\
-
\
- - \.
{«««««««««««««««««««««(§ §: :/ 3«« <<«<<<<<««<
»»»»»»»»»»»»»»»»»»»»»»§ §: j §»»»»»»»»»
8;\ j / / / / / / / / / / / / / / / / I
j 3"""'"" " " " " " " " " " " '",,'
""""'""'"""""""""" §: v j 5{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
.... ::=:::-jC ,,/
/ / / / / / / / / / / / / / / / / s: !; " / §H}}}}}}HH}}}}}H}}}}}}}}}}}}}}}}}}}}}}}}}}}
v j
;:; -jC '#:: '-2; V IA\ " / '""'
»»»»»»»»»»:: 8: * ;8. " / §»»»»»»»»»»»»»»»»»»»»)
g \! ;\ ::::: ;: \j !\ / §
t: / 0 : § §»»»»»»»»»>:2 t 0 " j 2
t: / " V (\ .,& ::j}: ::::::;; ::: -jC % Z: v !\ " / §
e / " 0 : § §// / / / / / / / / / / / / / / / /2 f 0 / g§
to j \ '0 §"""""""""""""""""" '!; j; \ j §
" j '0 ;§; : '0 ;\ \ j §
::: \ 8 : § \
:: / " v !\ .;3) : § §((({««((((«««««((((((«(«(((«((( is V / g§
g j : § : 0 j 2
(«((((«(«( («((((((((«««((«(((««(«:; :::::: i: % V " / <<< <<<<<<<<<<< < <<<
f §> >> >> > > > > > > > >> > > > >
0 j / / / / / / / / / / / / / / / /
H}}}}} '0 j '"" " " " " " " " "-
""""'"""""""""'""" 2: j
/ / / / / / / / / / / / / / / / / j §}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
««««««««««§ :,§; '0 j( :: // §««««««««««««««««««««(
>> > > > > > > > > > > > >> > > > > : 0 §)»»))»)»)»)»»)»)»))))»»)»»))
/ " V I> : -jC * ":::::J V /\ " /
/ " V i.\ ".>.:' *:::::::: ;; ;:::::: % l2j " /
- / " V 1\ "'\ * - ...»» »>'»> .... ;::::: -jC " /
/ " V 1\ * ::::::::: ,./ / _./ ./'. / --;;;::::: -l< V !\
; j -, ',8: ::j}: *:::::::::: ;; ....... -jC •.-...r \ f 1\ " /
j V ;\ : § §// / / / / / / / / / /// / / / /§ §: % /\ '" /
- // :\{t' :.1.'.' :* "-, " " " " " " """" " \?/ j
" .. - - ::=::: % <S. \! ;\ " /
j '0 '0 j
0 : §: .•, '0 ... j
0 v j
::::: §»)»)»)»»))»»»)»)))»))»))))»)))) §: 0 " /
,...... ,••
:\
/\
/\,...
/....
f ....
/\
/'\
,1\
,:\.
,/'\
,/\
\/
\'
'J
\'
\/
\/
'i
"/
\/
'!
\/
',,>
/ '"
-""
/ "'-,
'\ \\, \ '\ '\ \ ). ). \ .., \ \ \ \ \ \ \ \ , \ \ \ \ \ "" \ \ \ \ \:::::: ::::: .=:.:: ::,: // '",
I , '-yo-' _A••,
I))))))))))))))))))))))))))))))))))))) ;::: :';:;:':: ;;.;.;:: "
; \, :::::: :::::
t, k*******-****-********-**"',,1 - ,..
::<: "
/\ ################## :::: ::.:-::" .
:::::: ::::::: :.7::;:::; /
............, ..·.m.. ..., .....
1\ (1/((1 (0 (II ((( ((I (1/(( (il ((I (I/(! ((( (I:::::: ::::::::: :3.::.'
:,':<, : :,>':<:<,<::> ',:<<::, II i I /"
/\ .............. ............... -.-' ,......"-..
r, / / / / / / / / / / / / / /:--:: ::::::: '-.-'
\/
\,/
\!
\/
, ,.
\/
'y'
\,,/
\/
"
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\:2
)))))))))))))))))))))))))))))))))) § § § ""
:::::: ::::::
************************:::::: ::::: 2= :::=:
:::::: =::
#######.#:#:#./1'####### ;2
--."." ....-.... ...··v-.. ...............
::::::: :::::
() (it (Ii til (IL:::::: ::::: :: :::::;

:::::::
....--.. -v-' ,..-.....,
§
::::::: ::::: :::;::' ,:::::: \/
..... / ..... / .........'. / ./ / .......... ,/ /' / / ::::: :::=: / \1

.S

.,...
.,... '
- ---..

:$

eLI-

.,..... .
...

"\ \;;.- \ \
~ } ~ = : . :
...-----.,..----,

»»::-'>
///////////
'"'" '"'''., '"'"'"'"'""
.{ {{ {{ {{{{{{{{{{{{{{{{{{{{i
-}}}}}}}}}}}}}}}}}}}}}}}n.
««({««««««««(,
\
,'.0: .
CHAPTER
11
Implementation
.OvenJiew
//////////
"- '"'"'"'",- '"'"'"'""-
{{{{{{{{{{{{{{{{{{{{{{{
n}}}}}H}}}}}}}}}}}}}}}}}

)))))))))))))))))))))))
127
"\
"\
;. ~ .
,} ~ \ ~ \ ~ \
128 The Implementation
In order to better understand the reasons for many of the design features
of the Little Smalltalk system it is important to first consider what features
of the Smalltalk language force the implementation to be different from,
say, a Pascal compiler or an interpreter for BASIC. Among the more im-
portant aspects of the language, from the implementor's point of view, are
the following:
D Smalltalk is typeless
There is no notion of a "declaration" of identifier type in Smalltalk.
Any identifier can be used to refer to objects of any type and can be
changed at any time to refer to objects of a different type.
D Objects have unscoped lifetimes.
In an Algol-like language, such as Pascal, variables are either global
or local. Global identifiers exist all during execution and can thus be
assigned static memory locations. Local identifiers exist only as long
as the procedure in which they are declared is active. Since procedures
activate and deactivate in a stack-like fashion, a stack (sometimes
called fl an activation record stack") can be used to maintain local mem-
ory locations. In Smalltalk, on the other hand, objects exist 9utside of
procedure invocation (if we take message passing to be the! SmaIltalk
equivalent of procedure invocation) and may persist for indefinite pe-
riods of tilne. Thus, in Smalltalk, a stack-like allocation scheme is not
appropriate, and a different memory allocation policy must be used.
D SmaIItalk is interactive.
In common with implementations for many other modem program-
ming languages, such as APL, B, Prolog, or SETL, Little Smalltalk is
an interactive system. This means that not only is the user free to
create or modify identifiers at run time, but such basic features as class
descriptions may change dynamically during execution. Thus, if run
time execution speed is to be kept fairly consistent, no portion of the
system may be tied too strongly to any particular feature (such as a
class description) that may later be modified.
D Smalltalk is a multi-processing language.
As we saw in the last chapter, it is possible for a user to specify a
number of different processes and have them execute concurrently.
Thus the Little SmaIItalk system must make it easy to transfer control
from one process to another.
The following sections will outline some of the more important ways
in which the design of Little SmaIItalk deals with these features. The re-
maining chapters will then deal with the implementation in more detail.
Identifier lYpelessness
Implementation Overview 129
In an Algol-like language, such as PascaL all identifiers must have a de-
clared type known at the time the program is parsed, during compilation.
Thus, as memory locations are set aside, either at load time or at run time,
it is necessary to allocate space only for the values, since the type infor-
mation is known to the compiler and code can be generated accordingly.
value
In a typeless language, such as Smalltalk, the type of an identifier
generally cannot be determined at the time a program (or class description)
is parsed. The conventional solution is to associate with the memory for
each identifier a small tag that indicates the type of object being held in
the value field.
I tag I value
In languages where the number of data types is fixed and rather small
(such as many LISP implementations), this tag field can be similarly sJ;I1all,
for example, eight bits. In Smalltalk, on the other hand, the only notion
at all comparable to the concept of type is the class of the object indicated
by the identifier; the number of different classes that could be defined is
virtually limitless. Fortunately, for every class there is a unique object
maintaining information about the class, namely the class object. Thus
each object in the Little Smalltalk system can be tagged with a pointer to
the appropriate class object.
class pointer value
To determine if some operation (message) is appropriate for some
object, the system uses the class pointer to examine the class (and, via
another pointer in the class object, any superclasses) to search for an
appropriate method. The next chapter will explain the internal structure
of Little Smalltalk objects in more detail.
Unscoped Lifetimes
In PascaL as in many other languages, memory for variables in a
procedure is allocated when the procedure is invoked and can be released
when the procedure returns. If, for example, a procedure P calls a pro-
130 The
cedure 0, the memory for 0 will be allocated after the memory for P and
can be released prior to that of P (since Q must return before :P can return).
Thus a stack can be used, with new memory being allocated on top of the
st;lck as each procedure is entered (Figure 11.1).
In Smalltalk, we have already noted, objects can be created at any time
and may persist for an indefinite period of time. This calls for a more
sophisticated memory allocation protocol. Since physical memory is, on
systems,. rather limited, it is important that memory for objects no
longer being be reused for qewobjects. Thus, at any time, memory
can be viewed as a sequence of locations, some of which are being used
and others unpsed (Figure 11.2).
The memory manager is thus an important component of the Little
Smalltalk system. The memory manager handles all requests for memory
and notes when memory is no longer being used. This important portion
of the system will be described jn more detail in Chapter 12.
11.1 0 Static view of Pascal memory
t
stack
growing
upwards
memory
for
procedure
Q

for
procedure
p
bottom
of
stack
\ ",
\
\
>
. ( ~
Implementation Overview
Figure 11.2 0 Static view of Smalltalk memory usage
memory for
object nil
unused
memory for
object x
unused
memory for
object true
memory for
object false
unused
An Interactive System
131
The internal representation of Little Smalltalk objects represents a com-
promise between two competing goals. On the one hand, the representation
must be flexible enough to provide ease in creation, modification, and re-
moval of objects. On the other hand, it cannot be so general as to greatly
degrade efficiency. For example, if methods were kept in their original
textual form they could be easily modified. This, however, would seriously
slow the interpreter, requiring that it repeatedly parse statements prior to
execution.
The internal representation of objects was briefly discussed in an earlier
section and will be explained in more detail in the next chapter. An im-
portant special case, however, is the representation of objects of class
Class, which must include a representation of class methods. Note that
an interactive system, such as the Little Smalltalk system, must keep a
great deal more information around than a batch-like system, such as a
traditional compiler. Whereas a compiler can ignore and delete the textual
representation as soon as an adequate internal form has been constructed,
an interactive system must be able to regenerate the original text, if nec-
132
\
The Implementation
essary. This is most obvious in the case of class descriptions, where three
options present themselves. The first option is to re-generate a textual class
description from the internal form if required, for example, to edit the
class description. Another option is to keep both the internal representation
and the original source level textual representation in memory. This, how-
ever, would require too much memory for small machines. An efficient,
although slightly less general option is to keep as part of the internal form
of a class object the name of the file from which the class description was
read. The only restriction then is that class descriptions cannot be created,
but must exist in some file. If the user wishes to edit the class description,
the file is opened and edited, using a conventional editor.
Like nlost interpretive systems, the internal representation of the pro-
cedural (or executable) portion of a class, namely the class methods, can
be thought of as an assembly language for a special purpose virtual ma-
chine. Whereas the real machine on which the system is executing deals
with resources such as bytes and words, the virtual machine can deal with
higher level concepts, such as stacks, objects, and symbols. The assembly
language for this virtual machine is called bytecode and is discussed in
Chapter 13. The interpreter for bytecodes is described in Chapter 14.
A Multi-Processing Language
The fact that Smalltalk is a multi-processing language produces a number
of difficulties. You might think that if Smalltalk did not permit multiple
processes, even though objects could persist indefinitely, at least the mes-
sage passing protocol would exhibit a stack-like behavior. For example, if
the message one is passed to an object a, and the method associated with
that message passes a second message two to another object b, then the
second message must return before the first message returns. Thus storage
incurred as part of message passing, such as storage for arguments or
temporary variables, could be allocated and deallocated in a stack-like
fashion similar to activation records in a conventional language.
Unfortunately, this view is too simplistic. Even without multiple proc-
esses, the implementation of blocks causes problems. In order to execute
properly, a block must have access to the environment (including argument
and temporary variables) in which it was defined. Also, -a block can be
passed back as a result of a message or assigned to an identifier and thus
outlive the message in which it was defined. Even in the single process
case temporary and argument variables do not necessarily come into ex-
istence and die in a stack-like fashion.
The solution is to uniformly apply the techniques of the memory man-
ager to those objects corresponding to values that the user can see, such
as identifiers, as well as to internally generated objects, such as those that
correspond to conventional activation records. When a message is to be
Implementation Ove11Jiew 133
sent, an object of class Context is created. A context (Figure 11.3) is an
array-like object that points to the receiver of the message and the argu-
ment objects passed with the message. The context also provides space for
any temporary identifiers or internal parameters (such as for blocks) that
will be needed by the message.
Asearch is then made of the class descriptions to locate the bytecodes
associated with the method for the message. Once the bytecodes are found,
a second type of object, called an Interpreter, is created. The name In-
terpreter is a slight misnomer; a better name might have been
lnterpretableMethodReadyForExecution. Instances of class Inter-
preter point to the bytecodes they will execute, the context they will use
during execution, and an interpreter stack used by the virtual machine
that is executing the bytecodes (Figure 11.4).
When an interpreter executes a bytecode instruction that involves send-
ing a new message, a new instance of Interpreter is created and linked
to the existing interpreter, which then becomes inactive until the message
returns:
new old
interpreter interpreter
AProcess is then merely a pointer to an active interpreter. Since there
can be many processes, all active processes are linked together (Figure
11.5). The structure of the process handler will be discussed in Chapter 14.
Figure 11.3 0 A typical instance of class Context
t--------? receiver
context I-------?arguments
t---------+ temporaries and block parameter locations
134 The Implementation
Figure 11.4 0 An instance of class Interpreter
1---------+context
interpreter f------+bytecodes
1---------+internal evaluation stack
Figure 11.5 0 A linked list of Processes
1
process
interpreter
1
process
interpreter
2
process
interpreter
3
J
System Overview
Implementation Ovetview 135
Figure 11.6 0
The impkmentation of the Little Smalltalk system, like almost any large
software system, is a collection of interacting components. This section
will describe in broad terms the various components and their interrela-
tionships.
Figure 11.6 illustrates the major components of the Little Smalltalk
system and their control flow relationships. Central to the entire system is
the process manager. As we saw in Chapter 10, a process is a sequence
of L i t t l ~ Smalltalk statements plus the context information necessary to
interpret them correctly. The process manager maintains a queue of active
processes (recall that multiple processes can b ~ created by using the mes-
sage fork or newProcess), insunng that each process is given afair share
of execution time. One special process is the driver. The driver reads the
commands typed at the terminal by the user, cre'ates a process to execute
An overview of the Little SmaJltalk system
, .
Special
Objects
Class
Parser
Driver
InjtiaIizatiof"!
Termination
Process
Manager
Courier
Memory Manager
Primitive
Handler
Interpreter
Execution
'-----,- -
136
"\
\
\
':-
~ ' } ~ \ ~ ~ - : -
T/:ze Implementation
each, and places the process on the queue managed by the process man-
ager. Subordinate to the driver is a special module for reading and trans-
lating class descriptions into the form used internally by the Little Small-
talk system.
Internally, Little Smalltalk statements are kept in an internal form
called bytecodes. The interpreter is in charge of executing bytecodes and
updating the context for each process in an appropriate fashion. Bytecodes
representing primitive operations are processed by a special module called
the primitive handler. The primitive handler is the main interface between
the Little Smalltalk system and the underlying operating system. Also the
primitive handler manipulates objects, such as integers, reals, strings, and
symbols, for which the underlying representation is different from that of
II normalII Little Smalltalk objects. To do this, the primitive manager uses
a set of special object routines, each particular to a different type of object.
One of the most common tasks of the interpreter is the sending of a
message from one object to another. To accomplish this, instructions de-
scribing the message to be sent are given to the courier. The courier creates
an interpreter to evaluate the message and places it on the process manager
queue, suspending the sending interpreter until the receiving interpreter
has returned a value and terminated.
Underlying and pervading all portions of the system is the memory
manager. The memory manager is in charge of creating objects and keep-
ing track of which objects are currently in use and more importantly, which
objects are no longer in use and thus can have their storage reclaimed to
create subsequent objects.
Finally, the initialization and termination module is the routine first
given control when the Little Smalltalk system is started. The task of this
module is to set the values of certain global variables to the correct initial
states, including reading in the standard library of Little Smalltalk classes,
creating the driver process and placing it on the process manager queue,
and starting execution. When the user indicates that execution can ter-
minate, this routine then cleans up various object references kept in global
variables and, if required, produces statistics on memory utilization.
Subsequent chapters will describe in detail the design and implemen-
tation of each of these components.
\.
»»»»»>
//////////
""''''''''''''''''''''
{{{{{{{{{{{{{{{{{{{{{{{{{{
.}}}}}}}}}}}}}}}}}}}}}}}}r
«««««««««««(!
»»»»»
//////////
"'''''''''''''''''''''
~ {{{{{{{{{{{{{{{{{{{{{{{{{
n}}}}}}}}}}}}}}}}}}}}}}}}
r«««««««««««1
)))))))))))))))))))))))
CHAPTER
12
The Representation
ofObjects .
137
138 The Implementation
I
Fundamental to understanding the operation of the Little Smalltalk system
is an understanding of how objects are represented internally. This chapter
starts by describing the internal representation of most Smalltalk objects.
Afew classes of objects, the so-calledspecial objects, have a slightly different
representation since their memory must be able to contain non-Smalltalk
values. Examples of special objects are instances of class Integer or Sym-
bol. Following the description of special objects, this chapter concludes
with a description of the memory manager.
As an illustrative example of the representation of objects, consider a
class Aclass that includes, as part of its definition, instance variables i, j.
and k. Assume that class Aclass is a subclass of Bclass, which defines
instance variables k, l, and m. Class Bclass, in turn, is a subclass of Cclass,
which defines instance variables n, p, and r. Finally, class Cclass is a
subclass of Object (which defines no instance variables). This class -
superclass structure is sh9wn in Figure 12.1.
Suppose variable a refers to an instance of class Aclass. Next suppose
we pass a message,. dobedo, to a. This message, however, is not imple-
mented as. part of the class description for Aclass but is inherited from
class Bclass. When we execute the associated method in class Bclass, the
only instance variables available to that method will be those of class
Bclass not those of either classes Aclass or Cciass.
1
It would be convenient
if the representation of instances of Bclass (or Aclass) did not depend
1. Little Smalitalk differs from the Smalltalk-SO language in this respect. In the Small-
talk-SO system the instance variables from class Cclass are accessible.
Figure 12.1 0 The c1ass-superclass hierarchy
defines instance variables n, p, and r
defines instance variables k, I, and m
defines instance variables i, Land k
The Representation of Objects 139
upon the representation of Cclass, since at any time the class description
for Cclass could be modified, even to the extent of eliminating instance
variables.
Thus a basic problem in constructing an internal representation for
classes and objects is devising a scheme that permits the representation
of a method for a class in a manner that is independent of superclasses.
For example, how can we represent methods in class Bclass so that
changes to class Cclass do not force us to make changes also to class
Bclass. The solution in Little Smalltalk is for the structure of each object
to mirror its class structure. That is, the object a possesses a pointer to an
unnamed object that is an instance of class Bclass. (Since it is difficult to
discuss unnamed objects, let us call the unnamed object b-object.) Similarly
b-object would contain a pointer to an instance of class Cclass. Finally c-
object contains a pointer to an instance of class Object. Thus the structure
of an object can be described as shown in Figure 12.2. In an analogy to
the class-supercIass relationship, we call b-object a superobject for a and,
similarly, c-object a superobject for b-object. Henceforth our terminology
will be somewhat ambiguous. We will sometimes refer to the complete
structure as shown in Figure 12.2 as an "object" and other times use the
same term for each of the individual components. The context will deter-
mine the exact meaning of the term.
Each of the objects in Figure 12.2 may contain instance variables but
only variables appropriate to the class of the object. When creating an
object, we need determine only the number of instance variables for the
class of the object and need not consider any information contained in
Figure 12.2 0 The object-superobject relationship
includes instance variables n, P, and r
includes instance variables k, I, and m
&c1Udes instance variables i, j, and k
ref_count;
size;
*c1ass;
*super obj;
*inst_var[1];
140
\ ~
'\
\ \
~ \ .> ~ \
The Implementation
the superclasses. The structure of each object in the Little Smalltalk system
can therefore be described as follows:
reference count
number of instance variables
class pointer
super object pointer
instance variable 1
...
instance variable n
The reference count field maintains a count of the number of pointers
to the object and is used by the memory manager to discover when the
memory used by an object can be recovered (when the reference count
reaches zero). We will discuss this in more detail later in this chapter. The
class pointer points to the instance of class Class that contains the de-
scription of the class of which the current object is an instance. The super-
object pointer points to the instance of the superclass of the current object
class, as just described. (As a special case, instances of class Object contain
a null pointer in this field). An integer is contained in the object indicating
the number of instance variables the current object contains. The final
portion of each object is a list containing the values for each of the instance
variables in the object (which are, in truth, pointers to other objects).
Since C does not generate code to perform subscript checking on array
bounds, a single structure can be used to represent structures of any size,
as follows:
struct obj_struct {
int
int
struct class struct
struct obj struct
struct obj struct
}; -
The inst var array can be indexed arbitrarily to obtain any desired
instance variable.
2
There are two objects that can "represent" a receiver of a message. The
2. The purposeful abuse of arrays shown here is not presented as a principle to be widely
applied. It is in fact quite easily a source of very inscrutable errors, and great care must be
taken to insure that each time we index into the inst val' array valid information will be
found there. Tricks of this kind should only be used after careful consideration has removed
all more transparent alternatives. And, during development, code where tricks such as this
are used must be all the more carefully examined to insure each use of the inst_val' array is
correct.
The Representation of Objects 141
-
-
first is the object to which the .message was actually sent. The second is
the internal object which is an instance of the class in which the method
executed was found. In the example we have been using, a would be an
example of the first type, and the unnamed b-object would be an example
of the second. Both objects are important in understanding the meaning
of messages sent to self and to super. If the method for dobedo sent a
message to self, the search for the corresponding method would begin in
class Aclass. A message sent to super, on the other hand, would require
a search to begin in class Cclass. The class of the receiver of the original
message gives the location of the search for the former, whereas the class
(actually the superclass) of the object that actually responded to the orig-
inal message gives the location of the search for the latter. Thus both
objects must be available to the interpreter. We will discuss this more in
a later chapter.
Special Objects
Note that the instance variables in the objects described in the last section
are, at the level of C structures, merely pointers to other objects. Never-
theless, there must exist some objects in the Little Smalltalk universe with
memory containing not other objects but values that mimic values in the
underlying machine representation. Examples of such objects are integers,
floating point numbers, or symbols. Fortunately the number of suchspecial
objects is small and cannot be increased by the user without modifying
the system. Figure 12.3 lists the special objects in the Little Smalltalk
system. We will illustrate special objects by using the example of the class
Float, instances of which must be able to contain a C"double" value. Since
each instance of class Float is an object, it must contain a reference count.
At the very least, then, structures for class Float must contain the following:
struct float_struct {
int
double
};
The f prefix on the ref count field and others is used in deference to those
C compilers that demand unique field names on structures.
Although the variety of special objects is small, the number of instances
of these objects can be quite large, usually far exceeding all other types of
objects. Therefore, a concise representation for these objects can reduce
substantially the size of the entire data area and, in many cases, dramat-
ically alter the speed of the system or the size of the programs that can be
executed.
A basic problem is how to tell if an object is or is not a special object,
and, if it is, what type of object it represents. The "obvious" solution is to
"\.
\
;.
\
.,.
',.
142 The Implementation
Figure 12.3 0 Special objects in the Little Smalltalk system
Class
Block
ByteArray
Char
Class
file
Float
Integer
Interpreter
Process
String
Symbol
Use
code blocks
bytecode arrays
single characters
class descriptions
external files
floating point quantities
integer quantities
interpreters (bytecodes in execution)
processes
string values
symbolic values
keep a table with the class for each special object. By looking up the class
of any particular object in this table we can tell if it is special and what
type of object it is. This solution, however, is unworkable. The Little Small-
talk system makes no distinction between classes for special objects and
other classes, and, therefore, there is nothing that prevents a user from
altering a special value class. If the user does modify one of these classes
such as Float, then new instances of class Float should point to the new
class description. Nevertheless, instances of the old class Float must also
be recognized as special objects. In a single table it would be difficult to
keep enough information to recognize that both of these instances were
indeed special objects.
A non-obvious but more workable solution is to use the "size" field to
mark special objects. Normal objects will always have a size field that is
zero or greater. Since the memory of special objects does not provide for
the use of the inst_var array (and, by implication, special objects cannot
include instance variables), we can use a negative number to designate
special objects. Different negative numbers can be used to distinguish the
different types of special objects. Special objects are created either by the
driver calling C routines or by invoking primitive methods, and thus it is
easy to insure that proper numbers are maintained. Testing to see if a size
field is less than zero is sufficient to determine if an object is a special
object or not. Testing to see if it is a particular value will tell what qrpe of
object it is. We can define macros to perform these operations. For e ~ { ­
ample, suppose we choose - 31415 to represent the special objects of class
Float (the choice of number is unimportant, as long as it is distinct from
\
.
The Representation of Objects
L..- --'I
143
the numbers for all other special objects). We can define the following
macros:
# define FLOATSIZE -31415
# define is_bltin(x) «(object *) x) -'-'- >slze < 0)
# define check_bltin(obj, type) «(object *) obj) - >size = = type)
# define is_float(x) check_bltin(x, FLOATSIZE)
The macro is_bltin tests to see if an object is speciaL Note the use of a
cast to insure that the size field can be applied to the object. The macro
is_float determines if an object represents a instance of class Float.
Should special objects contain a class and/or a superobject pointer?
Arguments can be made both ways. For the sake of uniforrriity and con-
sistency the answer should clearly be yes. However, the ciass description
of special objects is not likely to change during execution (in contradiction
to some comments made earlier). Since special objects are by far the most
common form of object in the. system, a small reduction in the memory
requirements for special objects may have a considerable impact on the
total amount of memory needed for execution. By keeping a table of special
object classes and we can eliminate the necessity of keeping
this information with every object. This, of course, has the unfortunate
consequence that should the user redefine a class such as Float, all floating
point values that existed prior to the change will have their classes altered.
Nevertheless after considerable debate on this issue, it was decided to use
the more space-efficient representation in Little Smalltalk.
3
Internally, an instance of class Float has the following structure:
# define FLOATSIZE -31415
struct float-'.struct t
irit
int
double
};
f_ref_count;
f_size;
f_value;
The f_size field should always be FLOATSIZE. All other special objects
are treated similarly. The superobject and class of any special object can
be determined by a pair of procedures: fnd_super( ) and fnd_class( ),
respectively.
3. The fact that we wanted Little Smalltalk to run on machines with very limited mem-
ory, such as the DecPro 350 or the IBM PC, was a major factor in this decision. Also note
that this scheme works only because the superclasses for classes representing special objects
do not contain instance variables. In the one case where this is not true (the class String),
the representation for each object must contain a superobject pointer.
144 The Implementation
Memory Management
As execution progresses, objects are continually being constructed, used,
and discarded. If new memory were allocated each time an object was
constructed, the system would very quickly exhaust all available memory.4
Therefore in the Little Smalltalk system great care is taken to reuse memory
as much as possible. A method known as reference counting is used to
accomplish this. A reference to an object is simply a pointer pointing to
the object. Each object maintains a count of the number of currently
existing references that point to the object. Care is taken to keep these
counts accurate. When a reference count reaches zero, there are no re-
maining pointers that can reach the object, and therefore the memory it
occupies can be recycled for use by another object.
5
Free memory is maintained on free lists. A free list is a linear-linked
list of free memory structures. When memory for a new object is desired,
the free list for the object type is examined first. If a structure is found on
the free list, it is removed and used for the new object, otherwise, a general
memory allocation routine is called to allocate new storage for the object.
When the reference count on an object reaches zero, the object is returned
to an appropriate free list.
For normal Little Smalltalk objects (i.e., not special objects), a free list
is maintained for all objects containing less than a certain number of
instance variables. An array is defined, the elements of the array being the
head for a free list of objects of the given size. This array is obj_free_list,
shown in Figure 12.4.
Objects are created by calling a procedure new_obj( ), shown in
Figure12.4. New_obj takes three parameters. The first is a pointer to one
of the special objects representing a class, the second an integer indicating
the number of instance variables to be allocated in the object, and the
third a flag indicating whether the instance variables should be initialized
to the value of the pseudo variable nil.
6
4. Virtual memory systems postpone this problem but do not eliminate it. In addition,
since objects have different lifetimes, unless some provision is made for reusing object mem-
ory on a virtual system, serious thrashing can result.
5. There are two main classes of memory management algorithms, reference counting
schemes and garbage collecting methods. Reference counting has the advantage of simplicity,
which is the main reason it is used in the Little Smalltalk system. It has the disadvantage
that cycles can cause memory to be marked as being used when in fact it is not. A good
discussion of memory management algorithms can be found in (Knuth 81).
6. Careful readers will note a bit of circularity here. The pseudo variable nfl is an object
and is therefore presumably created by calling new_0 bj. Can nil be initialized to nil? Similarly
nil is presumably an instance of some class (UndefinedObject) that contains fields that must
be initialized to some value. Which comes first, the instance nil or the class UndefinedObject?
This difficulty is known as bootstrapping and is i n d e ~ d one of the tricky aspects of the Little
The Representation of Objects
Figure 12.4 0 The procedure new_obj()
# define MAXOBJLlST 100
struct obj_struct *obj_free_list[ MAXOBJLlST ] ;
# define sizeobj(x) (sizeof(object) + «x) - 1) * sizeof(object *) )
struct obj_struct *new_obj(nclass, nsize, alloe)
struct c1ass_struct *nclass;
int nsize, alloe;
{ struct obj_struct *new;
int i;
if (nsize < 0)
eant happen(2);
if (nsize < MAXOBJLlST && obj_free_list[ nsize ] ) {
new =obj_free_list[nsize];
obj_free_list[nsize] = new- >super_obj;
}
else {
new =(object *) o_alloe(sizeobj(nsize»;
]
new- >super_obj = (object *) 0;
new- >c1ass =nclass;
if (nclass)
obj_ine«object *) new- >c1ass );
new- > ref-,eount .=0;
new- >size = nsize;
if (alloe)
for (i = 0; i < nsize; i+ +) {
obj_ine(new- >inst_var[ i ] = o_nil);
]
return(new);
145
The value nsize should never. be negative (special objects are created
by other means, to be described shortly). The procedure cant happen() is
a bit of lfdefensive programming," designed to trap impossible situations
that somehow do happen. This routine is used throughout the Little
Smalltalk initialization sequence. The solution involves first creating some objects that do
not have a class and using them to create other objects which then can be used to overwrite
the first object. Eventually a complete system is produced.
\
':.
"\
\
.. } ~ \ ,~ ~ \
146
The Implementation
Smalltalk system. If cant_happen( ) is ever called, an informative error
message is produced and execution is halted.
After checking that the size is positive, the procedure new obj() next
examines the object free list of the appropriate size. If there is some object
on the free list, it is removed, and the list is updated. Note the use of the
super obj field as the link in maintaining the free list. If the number of
instance variables is too large or if there is nothing on the free list, a new
object is created by calling a general purpose memory allocation routine.
When the reference count on an object indicates that its memory can
be reclaimed, the routine free obj() (Figure 12.5) is called. Free obj frees
the instance variables used in-the object and then either places the object
on the free list or, if it is too large, returns it using the system memory
deallocation routine.
Each special object maintains its own free list. We illustrate this with
the free list routines for the class Float. The routine new_float( ) shown in
Figure 12.6 takes a C floating point value and returns a Smalltalk object
representing the equivalent value. The variable fr float contains the free list
for these objects and is declared to be of type mem struct, since the fields
of the floating structure do not contain anything that could be used as a
link. Acast is used to insure that variables are assigned values of the correct
type.
Figure 12.5 0 The procedure free_obj( )
free_obj(obj, dofree)
struct obj struct *obj;
int dofree;
{ int size, i;
size = obj->size;
if (dofree)
for (i = 0; i < size; i + + )
obj dec(obj->inst var[i]);
if (obj->class) -
obj dec«object *) obj->c1ass);
if (size <: MAXOBJLIST) {
obj->super obj = obj free list[size];
obj free IistTsize] = ob}; -
} - -
else {
free(obj);
}
}
The Representation of Objects 147
Everytime a new reference to an object is created, we must increment
the reference count field for that object. This is accomplished by a macro
obj inc() shown in Figure 12.7. Similarly, whenever an object reference
is deleted, the routine obj dec() is called. The procedure obj dec() dec-
rements the reference count for the object. If the resulting value is still
positive, nothing further needs be done, since there are still valid refer-
Figure 12.6 0 Memory allocation routines for the class Float
struct mem_struct {
struct mem_struct *mlink'
};
struct mem_struct *fr_float = 0;
/* new_float - produce a new floating point number */
struct obj_struct *new_float(val)
double val;
{ struct float_struct *new;
if (fr_float) {
new = (struct float struct *) fr_float;
fr_float = fr_float->mlink;
}
else {
new = (struct float_struct *)
o_alloc(sizeof(struct float_struct»;
}
new->f_ref_count = 0;
new->f_size = FLOATSIZE;
new->f_value = val;
return( (struct obj_struct *) new);
}
free_float(f)
struct float_struct *f;
{
jf (! is_float(f»
cant_happen(S);
«struct mem_struct *) f)->mlink = fr_float;
fr_float = (struct mem_struct *) f;
}
148 The Implementation
Figure 12.7 0 Object reference increment and decrement routines
# define obj_inc(x) «x) - >ref_count + +)
obj_dec(x)
strud obj_struct *x;
{
if (- -(x - > ref_count) > 0) return;
if (x - > ref_count < 0) cant_happen(12);
if (is_bltin(x)) {
switch(x - >size) {
case FLOATSIZE:
free_float«struct float_struct *) x);
break;
default: cant_happen(6);
}
}
else {
if (x- >super_obj)
obj_dec(x - >super_obj);
free_obj(x, 1);
}
}
ences. If the resulting value is negative, something is wrong with the sys-
tem, and cant_happen( ) is called. Otherwise the reference count field is
zero, and the space is recovered by calling either a routine specific to the
type of the object or the general memory recovery routine.
-
-
Optimizations
Memory management is a central task in the Little Smalltalk system. Be-
cause a large percentage of execution time is spent in performing this task,
a great deal of attention has been devoted to speeding up the operation of
the memory manager. In this section we will merely mention some of the
approaches taken, leaving the details of implementation to the imagination
of the reader or to those ambitious enough to dig through the code.
1. The underlying operating system memory allocation routines are
slightly more efficient on large blocks of memory than on small blocks.
\
.~ -
The Representation of Objects 149
One scheme is to allocate a large block initially, for example a block
equal to 100 floating point structures, and then to carve it up into little
pieces and place them on the free list. Almost all free lists used in Little
Smalltalk are initialized in this manner.
2. Some constants, such as small integers or the pseudo variable nil,
occur frequently and seldom change. Asingle value can be maintained
and reused as called for.
3. According to the superobject scheme described in this chapter, all
objects should end in an unnamed instance of class Object. Since class
Object does not define any instance variables, we can reduce memory
requirements substantially by keeping a single instance of this class
and sharing references. A similar trick is used for numbers, which
share a common instance of class Magnitude and Number.
"\
.':0:. _
»»»»»>
/////.//././)
"""', "" '-, "" """""""""
{{{{{HH{{{{{{{{{{{{{{{{1
-H}}}}}}}}}} nn}}}}}n}}
«««««{«««{{««,
/ .///././././././/
"""'"""""'"""""""'"
~ { { { { {{{{{{{ {{{{{{{{{{{{{{
}}}}}}}}}}}}}}}}}}}}}}}} }}
((({{((((((((l
)))))))))))))))))))))))
CHAPTER
13
Bytecodes
150
Bytecodes 151
Like most interpretive systems, the Little Sinalltalk interpreter represents
programs (in this case, class descriptions) internally in an intermediate
representation. The. word intermediate refers to the fact that the code is
between the very high level class description and the low level language
in which the machine is actually operating. There are several reasons for
this type of representation. One is compactness; the internal representation
of a class description can be much smaller than the character represen-
tation used by the creator of the class. This is important since, for example,
there are over 300 methods defined in the standard library alone. Asecond
reason is efficiency; by translating the class description once into an in-
termediate representation and thereafter using the internal form, we avoid
having to reparse the class description each time a method is invoked.
With a good intermediate representation, it will be possible to construct
a very fast interpreter.
This chapter will describe the intermediate representation for methods
used by the Little Smalltalk interpreter. This intermediate representation
is known as a bytecode formaLl
A traditional approach in designing interpreters is to define a virtual
machine for a simpier language and then translate the high-level language
into instructions for this simpler machine. Consider what such a virtual
machine mig1).t look like for the Smalltalk language. Assume that at run
time context information, such as the values of instance variables or tem-
porary variables, will be available in the form of an array. We can arrange
for this to be true and can define even the mapping between instance
variables (for example) and an index into the context array when the class
description is parsed. Aconvenient form of virtual machine is a stack-based
architecture. In this style, intermediate results and temporary values are
pushed onto or removed from a stack as required. Given these assumptions,
the actions we would like to perform are as follows:
1. Access or modify instance variables.
2. Access or modify temporary variables.
3. Access arguments.
4. Access literals. Two special subcases of this are pseudo variables (since
the meaning of self or super is context dependent and cannot be
assigned at the time the method is defined), and class variables (since
the class may not exist when the method is defined and the meaning
of class variables can only be established at run time).
1. Note that the bytecode format used in the Little Smalltalk system is different from
that used in the Xerox Smalltalk-80 system.
\ \
\
\
.,.
152 The Implementation
5. Send messages. Aspecial case of this is sending a message to self and
to super. )
6. Perform a return of some expression. A special case of this is the
implicit return of self at the end of every method.
7. Create a block.
8. Perform a primitive operation.
So there are approximately a dozen types of operations we would like
to perform. We will eventually define a few more to permit some optimi-
zations, but a limit of 16 different operations is sufficient. In each case
the operation can be described as a tag, or opcode (to pursue the virtual
machine analogy), followed by some other information. In many cases the
other information is simply an integer (an offset into the instance. variable
array, for example). In other cases the value is more complicated (a literal
or a class name, for example).
First let us consider how we might represent an operation such as
referencing an instance variable. We have hypothesized at least 12, and
no more than 16, different types of operations. So four bits are both nec-
essary and sufficient to represent the operation type. Since we will want
to sequence easily through a list of opcodes/values pairs and in these cases
the opcodes and values can be represented by small integers, an array of
bytes can be an attractive representation. It seems rather wasteful to devote
an entire byte to each opcode, since each opcode requires only four of the
eight bits in the byte. One scheme, therefore, is to encode both the opcode
and the value fields in a single byte, placing the opcode in the upper four
bits and the value field in the lower four bits. (Each four-bit sequence can
be called a unibble" since it is a small byte.) Pictorially, this can be rep-
resented as follows:
width
field
4
opcode
4
value
If, for example, we let 1, mean "access an instance variable," then the
instruction to access instance variable number 3 would be the single byte
with value I * 16 + 3, or 19. "'-'"
An obvious problem with this scheme is that it permits the manipu-
lation of only 16 instance variables. Classes can have more, but that is
quite uncommon. A simple solution to this, and related problems is to
have an "extended size" instruction. This would involve having some op-
code (say, zero) which takes another opcode as a value. The entire next
byte following this instruction is then taken to be the value field for the
extended instruction. Pictorially, the instruction "access instance variable
number 37" could be given as follows:
width
value
4
o
Bytecodes
4 8
37
153
The advantage of this scheme is that it can apply to more than just
one type of opcode, and, furthermore, it allows us to keep the very short
description in the large number of cases where the extended form is not
necessary. Of course, the disadvantage is that we are still limited to 256
instance variables, but that is a reasonable
Asimple solution to the problem of literals is to associate a literal array
with each method. At parse time this array can be defined with whatever
. literals are needed by the method. The value field for those instructions
that require a literal is then just an index into this array.
The following sections present a description of each of the instructions
in our internal representation.
Extended Instruction Format - opcode 0
width
value
4
o
4
I opcode I
8
value
The low order 4 bits of the first byte are used as the opcode for the next
instruction. The following byte (all 8 bits) are taken to be the value field
for the next instruction.
Access an Instance Variable - opcode 1
width
field
4 4
I index I
The instance variable indexed by the value field is pushed onto the stack.
The extended instruction format can be used for instance variables with
indices greater than 16.
Access an Argument or Temporary Variable - opcode 2
width 4 4
field 2 I index I
Both arguments and temporary variables are kept in a single array called
the context (Chapter 11). The element of this array indexed by the value
field is pushed onto the stack. As with the instance variable opcode, the
extended instruction format can be used for indices greater than 15. By
convention the receiver (the zeroth argument) is placed in the first position
of the context.
154
:. l.:. \
\
) '>
.The Implementation
Access a Literal - opcode 3
width 4 4
field 3 I index I
The element of the literal array indexed by the value field is pushed onto
the stack. Note that, since the literal array can hold any literal value known
at parse time, this works for all types of literals (characters, integer, string,
symbol, or arrays) with the exception of c,iasses, which must be generated
at run time.
Access a Class Object - opcode 4
width 4 4
field 4 I index I
At parse time, a symbol representing the name of the desired class is placed
into the literal array. The value field then contains the index in the literal
array of this symbol. During execution the class description corresponding
to this symbol is retrieved and· pushed onto the stack.
Store into an Instance Variable - opcode 6
width
field
4
6
4
I index I
The current value contained in the top of the stack is popped and stored
into the instance variable indexed by the value field.
Store into a Temporary - opcode 7
width 4 4
field 7 I index I
The current value contained in the top of the stack is popped and stored
into the position of the context indexed by the value field. Although both
arguments and temporary variables are stored in the context, the parser
can make certain that no instruction which would overwrite an argument
location is generated.
Send a Message - opcode 8
width
value
4
8
\
.\,
Bytecodes 155
It is assumed that prior to this instruction the receiver of the message
plus the necessary argument values have been pushed onto the stack. The
value field contains the number of arguments to be passed along with the
message. The extended instruction format can be used for messages with
more than 16 arguments. The following byte is interpreted as an index
into the literal array. The ·symbol stored at that location is taken to rep-
resent the message selector to be sent.
Send a Message to super - opcode 9
The fields are the same as in the previous message. Note that the object
representing both self and super is the same and is given by the first
position in the context array.
width
value
Create a Block - opcode 14
4 4 8
,--_1_4__1 argcountl argument location I
8
block size
The value field of the first byte contains the number of arguments for
the block. If this value is nonzero, the second byte contains the position
in the context where the arguments for the block should be placed. If there
are no arguments for the block, the second byte is omitted. The third byte
contains the size (in bytecodes) of the instructions contained in the block.
The instructions for the block follow immediately after this byte.
Special instruction - opcode 15
width 4 4
field 15 I value
The value field is used to indicate a variety of instructions that either do
not require arguments or usually require arguments greater than 16, and
thus would not benefit from the short encoding. These can be described
as follows:
value
1
2
3
4
5
6
meaning
Duplicate top of stack.
Pop top of stack and discard it.
Return top of stack.
Return from inside of block.
Return receiver.
Pop top of stack. If it is true, skip the number of bytes indi-
cated by the next byte and push nil onto the stack.
\ "\
\
\
)- ~ \ ..~ ~ \
156
7
8
9
10
11
12
The Implementation
Pop top of stack. If it is false, skip the number of bytes indi-
cated by the next byte and push nil onto the stack.
Skip forward the number of bytes indicated by the byte im-
mediately following this instruction.
Skip backwards the number of bytes indicated by the byte
immediately following this instruction.
Perform a primitive operation. The following byte indicates
the number of arguments to accompany the primitive; and
the byte after that, the primitive number.
Pop top of stack. If it is true, skip the number of bytes indi-
cated by the next byte and push true onto the stack.
Pop top of stack. If it is false, skip the number of bytes indi-
cated by the text byte and push false onto the stack.
The Representation of Methods
Opcodes 5 and 10 through 13 are used to provide a succinct representation
for common operations, and they will be described in the next section.
The class of special objects ByteArray is used to represent arrays of bytes.
Instances of ByteArray are created using a syntax similar to normal arrays,
with a square bracket instead of parenthesis:
#[ 17 23 36 ]
Internally, a method is translated into an array containing two ele-
ments. The first element is a ByteArray containing the bytecodes for the
method. The second element is the literal array associated with the method.
Thus, for example, the method:
isEmpty
t self size = 0
would be represented in the bytecode format in the following way:
highBits lowBits
2 1
8 0
1
4 2
8 1
Meaning
Push the fi rst element of context (self) onto stack.
Send a message with no arguments.
Message is at first location of literal array.
Push the literal in location two onto stack.
Send a message with one argument.
\
S. _
15
3
3
Bytecodes
Message is in third location of literal array.
Return top of stack.
157
literal array
#( # size 0 # = )
This would be rendered entirely in Smalltalk format as follows:
#( #[ 33 128 1 66 129 3 243 ] #( #size 0 # = ) )
Optimizations
There are two classes of optimizations. The first type reduces the size of
the internal representation of methods. Since the standard Smalltalk li-
brary is represented in the internal form, and is of considerable size, any
savings in size will greatly decrease the amount of memory that must be
devoted to storing the standard library and thus increase the size of pro-
grams the user can execute. The second class of optimizations increases
the speed of the Little Smalltalk system, while possibly limiting generality.
To understand why more succinct representation is necessary, consider
the representation of the method for isEmpty described in the last section.
Some constants, such as 0, 1, or nil, occur with much greater regularity
than do any other constants. One way to reduce size, therefore, is to encode
with a special opcode a few of the most common integers, classes, and the
pseudo variables. This is essentially trading a small increase in the com-
plexity of the interpreter for a reduction in the size of many methods. The
value field of this special.opcode should be able to represent the most
common integers ( -1, 0, 1,2), common classes (Array, Collection), and
the pseudo variables nil, true, false and smalltalk. We will use opcode 5
for this purpose. The following table shows how each of the values is
interpreted.
0-9 The integer value.
10 The integer -t
11 The pseudo variable true.
12 The pseudo variable false.
13 The pseudo variable nil.
14 The pseudo variable smalltalk.
15 The pseudo variable selfProcess.
30 One of the classes Array, Arrayedcollection, Bag, Block, Boolean,
ByteArray, Char, Class, Collection, Complex, Dictionary, False,
158
\ "\:. \
\
); ~ \ ...-}
.,.
The Implementation
File, Float, Integer, Interpreter, Interval, KeyedCollection, Magni-
tude, Number, Object, Point, Radian, Random, Sequenceable-
Collection, Set, String, Symbol, True, or UndefinedObject
Note that the class constants have value fields greater than 16 and thus
must use the extended instruction form. Nevertheless, there is still a net
size reduction since these constants are much less common than the in-
teger values, and the form still eliminates the need for a position in the
literals array.
In a similar fashion, when we examine the bytecode representation of
a few classes, we note that some messages occur with much greater fre-
quency than others. For example, in the class Collection the messages
new new: value: do: class and error: comprise almost a third of all
messages relayed. The. distribution of messages sent will, of course, differ
from class to class, but the following messages seem to be most common:
unary messages
new isNil notNil size class value first next print printString strictlyPositive
currentKey
binary and binary keyword messages
new: at: to: do: value: = = ~ ~ timesRepeat: whileTrue: whileFalse: ifTrue:
ifFalse: error: add: coerce: removeKey: addFirst: addLast: reverseDo: addAII:
addAIILast: occurrencesOf: remove: binaryDo: keysDo: inRange:
arithmetic messages
+ - * " "" bitShift: bitAnd: bitOr: < < = = ~ = > = >
ternary keyword messages
at:put: ifTrue: ifFalse: ifFalse:ifTrue: value:value: to:by: at:HAbsent:
indexOf: ifAbsent: inject: into: remove: ifAbsent: removeKey :ifAbsent:
A very powerful scheme for reducing the size of the internal bytecode
representation of many methods is to encode the sending of these common
messages by a single instruction, using the value field to indicate which
instruction is desired. Note that this does not alter the meaning of the
message or the way in which the message is processed by the receiver; it
merely reduces the size of the bytecodes and of the literal arrays. We will
use opcode 10 to represent unary messages, opcode 11 for binary messages,
opcode 12 for arithmetic messages, and opcode 13 for ternary keyword
messages.
If we use these new opcodes, the bytecode representation for isEmpty
becomes the following:
highBit
2
10
5
lowBit Meaning
1 Push the first element of context (self) onto stack.
4 Send unary message lI size.
'1
o Push constant O.
12
15
literal array
#( )
10
3
'.
.\.
Bytecodes
Send arithmetic message 11=."
Return top of stack.
159
So the size of the bytecode array has been reduced from 7 to 5 and, more
importantly, the literal array has been eliminated altogether.
Dynamic Optimizations
The optimizations we have been considering so far have been concerned
with reducing the size of the internal representation. An even more im-
portant type' of optimization is concerned with increasing the speed of the
Smalltalk system. The next section will consider several of these optimi-
zatjons.
A great percentage of all messages processed by the Smalltalk system
are represented by ifTrue:, ifFalse:, or their combinations. Conditionals
and loops can be implemented using nothing more than message passing.
While this contributes to the simplicity and elegance of the Smalltalk
language, from apractical point of view a considerable amount of time is
being needlessly used by the system in the overhead involved in message
passing.
, One scheme that improves the speed of the Smalltalk system is to
process conditionals and some loops with in-line code.
2
Consider the se-
quence of actions to be performed in a conditional expression:
(3 < 7) ifTrue: [9]
In the process of interpreting this expression, the boolean expression would
be evaluated and pushed onto the stack. The top element of the stack would
then be removed, and the message ifTrue: would be sent to it along with
a block argument.' Either the block would be evaluated and its result re-
turned or nil would be returned by the appropriate subclass of Boolean.
In either case, the result would be pushed back onto the stack.
2. There is, of course, a great philosophical debate concerning whether this is desirable.
According to some, in Smalltalk the user should be able to change arbitrarily the meaning
of any message, the meanings of iffrue: and ifFalse: for example. Further-
more, since there is no notion of types in Smalltalk, the parser cannot be certain that the
recipient of any iITrue: or ifFalse: message will be an instance of Boolean. In some other
class, the meanings of these messages could be' radically different. Nevertheless, the speed
increases are so dramatic that some compromise must be maqe between maintaining the
elegance and increasing the efficiency of the language.
\ \
\
';.
"
~ \
.' .'
160 The Implementation
Instead of using message passing, the interpreter can simulate these
actions by using a "skip" instruction, called "skip on false." The value field
of this instruction encodes the number of bytes to skip. The meaning of
this instruction would be to pop and examine the top of the stack, and, if
it is true, the instruction terminates and the next bytecode is examined.
If, on the other hand, the top of the stack is not true, the value nil is
pushed onto the stack and the location counter is incremented by the
amount in the value field. There is a similar lIskip on true" instruction. The
body of the argument block can then be placed irnmediately after the skip
instruction without creating a block.
Unfortunately, there are no remaining opcodes. Using opcode 15 we
therefore define skip on true and skip on false to be special instructions.
The following byte is then taken to be the value field, just as it is in the
extended instruction format. Note that even with this format, the repre-
sentation of a conditional is still shorter than the previous representation
using blocks and message passing.
Given this scheme, the internal representation of our example would
be as follows:
highBit
5
5
12
15
5
15
lowBit
3
7
8
7
1
9
2
Meaning
Push the constant 3 onto the stack.
Push the constant 7.
Send the message # <.
Skip on false.
Amount to skip.
Push the constant 9 onto the stack.
return top of stack
The implementation of the logical connectives and: and or: is similar
to that of conditionals, except that, instead of pushing nil onto the stack
when a skip is taken, either true or false is used. To accomplish this
another pair of special instructions, and-skip or or-skip, is used.
Finally by introducing another pair of instructions, which merely
branch forward or backward by a specified amount, it is possible to encode
whileTrue: or whileFalse:. The statement
[block one] whileTrue: [block two]
can be represented internally as
bytecode representation of block one
skip on false
bytecode representation of block two
skip back to the beginning of block one
1\
(8)
* 1\ (81

*

*
i\
{81
*
1\
*
f\
(2:

* 1\


* f\
*
;\
(8;

*
" f\

*
!\

*
;.;....c:
/\

*
* /\
(8)

*
,
1\
*
,
/'
;8
::H::
* -\
*
/\

*
/\ @

*

* /\
(81
*
!\
*
"
«««««
»»»»»>
//////////
'"'"'"'"'"'"'"'"'""-
{{{{{{{{{{{{{{{{{{{{{{{{{1
.}}}}}}}}}}}}}}}}}}}}}}}}}
«««««««««««(;
.»»»»»
//////////
,,"''''''''''''''''''''''"
{{{{{{{{{{H
}}}}}}}}}}}}}}}}}}}}}}}}}}
{«««««««««««!
)))))))))))))))))))))))
CHAPTER
14
The Process
Manager
161
\. -
162
\ ;;.
\
\
,>
.'
..--=-:
The Implementation
As Chapters 10 and 11 noted, the process manager is a central component
in the Little Smalltalk system. Acting as a controller, the process manager
schedules the different tasks to be performed and insures that every process
is given a fair share of execution. This chapter describes the interface
between the process manager and the rest of the Little Smalltalk system,
and it explains the tasks the various routines perform.
In a certain sense, the process manager can be thought of as an abstract
datatype manipulating a circularly (and doubly) linked list of process ob-
jects (instances of class Process). There is a global variable, running-
Process, that points to the process curreptly being given control of
execution. Each process points to a linked list of interpreters. The inter-
preter indicated by runningProcess points to the bytecodes, and it is the
bytecodes that are actually being executed ~ t any time.' This sit,uation is
shown' in Figure 14.1. The -doubly linked list controlled by the process
manager -is known as the process queue.
1. Except when the current interpreter is the driver, which we will describe shorty. In
the Little Smalltalk system, the actions of the driver are controlled by C code and not by
bytecodes.
figure 14.1 D The process queue
runningProcess
process
interpreter interpreter
1
\
process
interpreter
2
process
interpreter interpreter ' interpreter
3
I
\
)...
\
.'i:.
The Process Manager
\
.\
163
Processes (instances of class Process) are special objects and thus can
have an internal structure different from other types of objects. The in-
ternal C structure of a process is shown in Figure 14.2. The global variable
runningProcess has already been described. A second variable, current
Process, is slightly different. The variable currentProcess is guaranteed al-
ways to point to a process that is on the process queue. If the currently
running process becomes terminated, the value of runningProcess will not,
change until control is returned to the process manager; however, the value
of currentProcess will be moved to the next value in the process chain.
Processes can exist without being on the process queue. Passing the
message newProcess to a block, for example, creates a process but does
not schedule it for execution. Processes are said to be in one of four states.
These states are:
Active An active process is one that is on the process queue and
will be scheduled for execution.
Figure 14.2 0 The internal representation of processes
struct process_struct {
int
int
struct interp_struct
int
struct process_struct
struct process struct
}; -
p_ref_count;
p_size;
*interp;
p state;
* next;
*prev;
extern struct process_struct * runni ngProcess;
extern struct process_~ t r u c t * cu rrentProcess;
extern struct obj_struct *0_drive;
# define is_driver(x) (x_drive = = (object *) x)
/* process states */
# define ACTIVE
# define SUSPENDED
# define READY
# define BLOCKED
# define UNBLOCKED
# define TERMINATED
o
1
- SUSPENDED
2
- BLOCKED
4
\ \ '. '.
: ' ~ ~ \
;}
.\
164
Figure 14.3 D Process manager interlace
The Implementation
init_process( )
start_execution ( )
link_to_process(anlnterpreter)
cr_process(anl nterpreter)
flush_processes( )
set_state(aProcess, state)
create initial process queue
start process manager
change interpreter link on current
processs
create a new process
remove all remaining processes from
queue
set the state on the given process
Suspended A suspended process is not on the process queue but can be
scheduled for later execution by passing it the message re-
sume. Newly created processes using the newProcess mes-
sage are initially in the suspended state.
Blocked A process can be blocked by a semaphore (see Chapter 10).
Like a suspended process, a blocked process is not scheduled
for execution. A blocked process may be restarted by the
blocking semaphore.
Terminated A terminated process is one that has halted either because
it finished execution or because it received an explicit ter-
minate message. A terminated process cannot be restarted.
A process that is not suspended is said to be ready. A process that is
not blocked is said to be unblocked. In terms of C subroutine calls, the
interface to the process manager is shown in Figure 14.3. The next section
will describe the purpose of each routine by going through a typical se-
quence of calls.
Initially, when the Little Smalltalk system is started, there are no ob-
jects on the process queue. The initialization module creates an interpreter
object (an instance of class Interpreter). This special interpreter is known
as the: driver. It is unique because, instead of having its actions controlled
by bytecodes, the actions of the driver are produced by a C subroutine
that reads commands from the terminal and creates other interpreters to
execute them. Internally, the driver is pointed toby a global variable named
o drive.
\
s.
\.
.\.
The Process Manager 165
Figure 14.4 0 Routines for performing process initialization
/* init process - initialize the process module */
init process ( )
{ - struct process_struct *p;
int i;
/* make the process associated with the driver */
currentProcess = cr process(o drive);
assign(cu rrentProcess - > next, CUrrentProcess);
assign(currentProcess - >prev, currentProcess);
cu rrentProcess - > p_state = ACTIVE;
/* cr process - create a new process with the given interpreter */
struct process struct * cr process (ani nterpreter)
struct interp struct *anlnterpreter;
{ struct process_struct *new;
/* get a process either from the free list or from memory */
if (fr_process) {
new = (process *) fr process;
fr_process = fr_process - >next;
}
else
new = structaIloc(process_struct) ;
new->p_ref_count = 0;
new- >p_size = PROCSIZE;
sassi 9n(new- > i nterp, anInterpreter);
new->p state = SUSPENDED;
sassign(new- > next, (process *) 0 nil);
sassign(new- >prev, prev, (process*) 0_nil);
return(new);
The initialization module calls init process( ), which in turn calls
cr process( ) to create a new process (Figure 14.4). The procedure init
process( ) then creates the initial process queue, with the single process
and interpreter, as follows:
166
\ \
'\
\
,
"
)
The Implementation
runningProcess - - - - - I ~
driver
interpreter
The initialization code then calls start executione ). The procedure
start execution() loops over the process queue and for the remainder of
execution will select items from the process queue and execute them (Fig-
ure 14.5). The flagatomcnt is used to provide "atomic" (i.e., uninterruptible)
execution and will be described shortly. When the user indicates there are
no further commands (by typing control-D), start execution() will return
to the initialization routine. The initialization routine will call flush proc-
esses( ), which will remove any remaining processes from the process
queue and execution will halt (Figure 14.6).
Before that happens, however, it is likely the user will type a number
of commands. When the driver is given control (via test_driver( ), as shown
Figure 14.5 0 The main execution loop
/* start execution - main execution loop */
start execution ( )
{ struct interp_struct *presentlnterpreter;
atomcnt = 0;
while (1) {
/* advance to the next process unless atomic action flag is on */
if (!atomcnt)
runningProcess = currentProcess = currentProcess->next;
if (! is driver(runningProcess->interp» {
/* notthe driver, resume executing the bytecodes */
sassign(presentl nterpreter, runningProcess->interp);
resu me(presentl nterpreter);
}
/* test driver is passed 1 if it is the only process
or if the atomic action flag is enabled */
else if (! test_driver«currentProcess = = currentProcess->next) II (atomcnt > 0»)
break;
The Process Manager
Figure 14.6 D Process termination
1* flush processes - flush out any remaining process from queue *1
flush processes ( ) .
{- .
whiIe (currentProcess ! = cu rrentProcess - >next)
remove process(currentProcess) ;
I*prev link and next link should point to the same place now.
In order to avoid having memory recovered while we are
manipulating pqjnters, we increment reference count, then change
pointers, then decrement reference count *1
obj inc«object *)
safeassi gn(currentProcess - >prev,(process *) 0 nil) ;
safeassign(currentProcess - > next, (process *)0 nil;
obj_dec«object *) currentProcess); -
1* remove_process - re!ll0ve a process from process queue *1
static remove process (aProcess) -
process *
{
if (aProcess = =aProcess - > next)
cant_happen(15); 1* removi!1g last active process *1
167
/* currentProcess must always point to a process that is on the process queue, make
sure this remains true *1
if (aProcess = = currentProcess)
currentProcess =currentProcess - >prev;
obj_inc«object *) currentProcess); obj_inc«object *) aProcess);
safeassign(aProcess - > next - > prev, aProcess - >prev);
safeassign(aProcess - >prev - >next, aProcess - > next);
obj dec«object *) currentProcess); obj dec«object *) aProcess);
- . . -
in Figure 14.5; the procedure test driver( ) will be described in the next
section) it waits for a command to be entered at the terminal. When the
user has typed a command, a new instance of Interpreter is created to
evaluate it. (The next chapter will discuss how interpreters are created and
executed in response to messages). The driver sets a pointer in the new
\
\
\
\
,'.0-
.,
168 The Implementation
interpreter to point back to itself and then calls link_to_process( ), giving
the new interpreter as argument.
The procedure link to process() changes the interpreter pointer on
the currently executingprocess (the one pointed to by runningProcess) to
be the argument (Figure 14.7). Thus, we have the following picture:
runningProcess
I
new driver
~ process
interpreter interpreter
I
runningProcess
Control then returns to start execution() which, if there were other
processes on the queue, would give control to the next process. If there
are no other ready processes, the newly created interpreter is given control.
Suppose the method being executed by the interpreter requires a. mes-
sage to be sent. The interpreter signals the courier that a message is re-
quired. After determining the recipient of the message, the courier creates
a new instance of Interpreter to respond to the message and links this
interpreter to the sending interpreter. The courier then calls link to proc-
esse ) to modify the current processes interpreter chain, giving us the
following picture:
I
process
receiving sending
driver
interpreter interpreter
I
Figure 14.7 0 The procedure Iink_toyrocess
/* link to process - change the interpreter for the current process */
link to process (anlnterpreter)
struct interp struct *anlnterpreter;
( struct obJ_struct *temp;
safeassign(runningProcess->interpI anInterpreter);
The Process Manager 169
'.
.\.
The execution of a block (generated by sending the message value to
an instance of class Block) causes a similar sequence of events. A new
interpreter is created to execute the statements within the block and then
is linked into the current interpreter chain.
When the interpreter encounters a bytecode indicating that a return
should be performed, it again calls link to process( ); this time however,
it passes as argument the interpreter towhich control is being returned.
runnin9Process----+I process
A return from within a block is slightly more complicated because the
return must take place from the context in which the block was defined.
, This context corresponds to an interpreter that may be several positions
higher in the interpreter chain. A search is made of the interpreter chain
until the correct interpreter is found, and then a return is performed from
that location.
Passing the message newProcess to a block causes both a new inter-
preter and a new process to be created. Unlike the original interpreter
chain, the interpreter chain for this new process ends not with the driver,
but simply with a null pointer for the calling interpreter. Passing the mes-
sage resume to this new process will place the process on the process queue.
When an interpreter chain ending in a null pointer is terminated (for
example, by finishing execution) the assoociated process is removed from
the process queue. The message fork passed to a block is simply a com-
bination of newProcess and resume.
The final procedure describe in Figure 14.3, set state(), is used by the
classes Process and Semaphore (via primitives) toinsert or remove proc-
esses from the process queue and to terminate processes in error conditions
(attempting to return from a block when the creating context is no longer
in existence, for example). The procedure set state( ) is shown in Figure
14.8. Because the classes Semaphore and Process themselves manipulate
the process queue, there is the potential for dangerous interaction should
the process queue change while a method in one of these classes is
170
\,
\

\
S
.,.
The Implementation
runningProcess ------..l--,P_r_0-rc_e_ss-'I---'----.i interpreter 1-----.) ...

new
process
1----+1 interpreter 1------. II nuII poi nter"
executing. For reason, there is a flag called the atomic action flag that
can be set by processes. If the atomic action flag is set by invoking the
proper primitive no other process will be given control of execution until
the atomic action flag is reset. Thus the class Semaphore, for example, will
enable atomic actions, insert or delete a process from the process queue,
and disable atomic actions. In terms of the process management routines,
atomic actions are controlled by the global variable atomcnt (Figure 14.5).
The Driver
The structure of the driver module is shown in Figure 14.9. The in-
terface to this module is through the single procedure test driver(),
2
which,
as we saw in Figure 14.5, was called by the start up routine. The procedure
test driver() is called by the process manager to determine if a command
has-been entered by the user at the keyboard. To do this, test_driver( )
calls upon the procedure line grabber(). The line grabber routine buffers
characters typed by the user until a complete line has been entered. When
the line grabber indicates that a complete line has been entered (by re-
turning a nonzero value), the first character of the line is examined by the
test driver() routine. If the first character is a right parentheses, the line
is assumed to represent a system directive and is passed to the command
module for processing. See Figure 14.10.
The command module examines the second character of the line to
determine the command type and then processes the command. This may
have the side effect of altering the location from which the line grabber
2. This is not quite true. The initialization module uses some of the subroutines from
the commands submodule during initialization, for example, to read in the standard library.
Figure 14.8 0 The procedure set_state()
The Process Manager
\
,;.,
171
/* set_state - set the state on a process, which may involve inserting
or
removing it from the process queue */
int set state (aProcess, state)
struct process_struct *aProcess;
int state;
{
switch (state) {
case BLOCKED:
case SUSPENDED:
case TERMINATED:
if (aProcess->p state = = ACTIVE)
remove process(aProcess) ;
aProcess->p state I = state;
break; -
case READY:
case UNBLOCKED:
if «aProcess->p stateAstate) = = -ACTIVE)
schedule process(aProcess);
aProcess->p state &= state;
break; -
case CUR STATE:
break;
default:
cant_happen(17);
}
return(aProcess->p_state);
reads input (in the case of the )i, )e and )r commands, for example), or of
totally changing the values in memory (for the )1 command. The )i com-
mand will generate a new Unix process to parse the class description given
in the command and then change the file examined by the line grabber to
be the output of the class parser. This will be described in more detail in
the next section.
If the line returned by the line grabber is not a command line, it is
-passed to the command line parser for decoding. The task of the parser is
to decipher the command line and produce an interpreter that will have
the effect of performing the actions desired by the user. To accomplish
172
\
,;0.
\
The Implementation
Figure 14.9 D Structure of the driver module
test
driver
line
grabber
line
parser
commands
module
this, a simple recursive descent parser is used. If no errors are found during
parsing, the parser calls upon the interpreter module to create a new
interpreter and then calls link to process() to place the interpreter onto
the process queue, as described in the last section.
The sources for the line grabber, the parser, the lexical commands
module, and the class parser described in the next section will not be
presented here since they are quite lengthy.
The Class Parser
Class descriptions are handled in a rather novel way in the Little Small-
talk system. Instead of complicating the driver by requiring it to read and
understand the syntax of classes, the systemcreates a separate Unix process
that parses the class descriptions and translates them into sequences of
simple Little Smalltalk statements. Since the class parser lives in its own
Unix process, its size does not increase the size of Little Smalltalk itself,
and the Little Smalltalk system can run on machines with very limited
address spaces.
3
The limitation of this technique, however, is that class
descriptions must be read in from a file and cannot be created directly at
the terminal.
The class parser uses an LALR parsing algorithm, generated auto-
matically from the grammar by a sophisticated parser generator. Since the
grammar is much larger, the class parser is many times more complex
than the simple parser used in the driver module. The class parser reads
3. The tradeoff is that the Little Smalltalk systemwill work only under operating systems
that permit multiple-user processes.
The Process Manager
Figure 14.10 0 The procedure test_driver( )
173
c_ref_count;
c size
*class name;
*super class;
*file_name;
/* test driver - see if the driver should be invoked */
int test driver(block)
int block; /* indicates whether to use block or non-blocking input */
(
switch(line grabber( block» (
default: cant_happen(17);
case -1:
/* return end of fi Ie indication */
return(O);
case 0:
/* enqueue driver process again */
return(1);
case 1:
if (*Iexptr = = 1)1) (
dolexcommand(lexptr);
return(1);
}
parse( );
return(1);
and parses a class description. If there are no errors encountered during
parsing, it then produces a file of Little Smalltalk statements that together
create the objects of class Class corresponding to the class descriptions.
The file containing these commands is then read by the line grabber mod-
ule, and the class is defined.
To understand how instances of class Class can be created using Little
Smalltalk statements, it is necessary first to see how objects of class Class
are represented internally in the Little Smalltalk system. Instances of class
Class are special objects (see Chapter 12), and thus are permitted to have
internal representations different from other Little Smalltalk objects. In
particular, the internal structure of Class objects is given by the following
C structure definition:
struct class_struct {
int
int
struct obj struct
struct obj-struct
struct
174
struct obj struct
int
struct obj struct
struct obC:struct
int
};
The Implementation
*c_inst_vars;
context size;
*message names;
*methods;
stack_max;
As we noted in Chapter 12, the c size field is always a designated
negative integer, the value of which indicates that this is an object of class
Class. The class_name, super_class and file_name fields are each pointers
to objects of class Symbol, representing, respectively, the name of the
class, the name of the super class, and the name of the file from which
the class description was read.
The c inst vars and message names fields are both pointers to arrays
of symbolS. The first represents the names of the instance variables defined
by the class (and, by the size of this array, the number of instance variables
used by the class). The second contains the names of the messages to
which the class will respond.
The methods field is a pointer to an array containing the internal
representation of the method associated with each message. This array
runs in parallel with the message names arrays, so to find the method
associated with a particular message you first look up the message name
in the message names array and then, using the same index, extract the
associated method. As we noted in the last chapter, each method is rep-
resented internally by an array of two elements. The first element is a
ByteArray containing the bytecodes for the method. The second element
is an array of literal values used by the method.
The final two fields, context size and stack max, are used in con-
structing interpreters to respond to messages accepted by the class. They
give the maximum size of the context and the stack needed to respond to
any message defined in the class. As the next chapter will describe in more
detail, each instance of class Interpreter independently maintains its own
stack of intermediate values.
Instances of class Array and class Class can be created by using prim-
itive operations. For this is important for bootstrapping purposes because
the first classes created cannot have access to any methods defined in other
classes. In particular, four primitives are important. Primitive number 110
creates an array of a specified size. Primitive number 112 assigns a value
to a given position in an array. Primitives 97 and 98 create new instances
of class Class and insert them in the class dictionary. Thus for a class
description such as the following:
Class test1 :Test2
I abc I
[
first: x
a+-x+3
second I i I
i+-a*7.
t i - 33
The Process Manager
\.
175
The following Little Smalltalk statements are generated. First primitive
110 generates an array of sufficient size to hold the methods for the class.
Primitive 112 then places the description of each method into the appro-
priate location in this array.. Note that no message passing is involved and
so these commands can be the very first ones executed by the Little Small-
talk system.
temp +- < primitive 110 2 >
<primitive 112 temp 1 II second II "-
#( #[ 16 87 194 113 33 48 193 243 245] "-
#( 33 ) ) >
<primitive 112 temp 2 II first: II "-
#( #[33 83 192 96 245] "-
#(» >
Primitive number 97 creates a new instance of class Class, initializing
it with the values taken from its arguments. Primitive number 98 takes an
object of classClass and places it into the internal class dictionary main-
tained by the Little Smalltalk system. Together, these two primitives can
define a new class, as follows:
<primitive 98 #Test1 "-
<primitive 97 #Test1 #Test2 #test1.st "-
#( #a #b #c) "-
#( #second #first: ) "-
temp 2 3 > >
When read by the Little Smalltalk system, these commands will define the
class Testl and provide it with the methods given by the class description.
"\
.'
~ » » » » »
//////////
"""""""""""""""""""
{{{{{HH{{{{{{{{{{{{{{{{,
}}}}}}}}}}}}}}}}}}}}}}}n:
«««««««««««(;
»»»»»
//////////
,,""""""""""""""""'"
~ { { H{{{{{{{{{{{{H{{{{{{{
~ } } } } } } } } } } } } } } } } } } } } } } } } }
((((((((((((i
\)\\)))))))))))))))\)))
CHAPTER
15
The Interpreter
176
\ \ "\ \
,.
): ..~
The Interpreter 177
As we have previously noted, methods are internally represented by byte-
code format (Chapter 13). When a message is sent, an instance of class
Interpreter is constructed. This object collects the necessary components
for executing the method associated with the message, namely the byte-
codes for the method, the receiver of the message,l the literals and context
needed by the method, and the stack to be used by the virtual machine
executing the method.
The internal C structure for instances of class Interpreter is shown
in Figure 15.1. Note that there are several more fields in addition to the
ones alluded to in the last paragraph. The sender field points to the sending
interpreter, that is, the interpreter that was active at the point the message
was sent which caused the current interpreter to be created. The creator
field is usually null, except in the case of interpreters created to execute
blocks. In this case, the creator points to the interpreter in which the block
was originally defined, and thus the interpreter to be returned from in the
case of a block return. The currentbyte pointer indicates the next bytecode
to be evaluated when execution continues. In fact it is a pointer into the
array associated with the bytecode field (Figure 15.2). In a similar fashion,
the stacktop is a pointer into one entry of the array stored in the object
pointed to by the stack field.
1. Actually both of them, since, as we saw in Chapter 11, there are two objects that can
be said to represent the receiver of a message. In Little Smalltalk the named receiver is
represented by the first position in the Context array, The actual receiver (i.e., the object in
which class the method was found) is explicitly pointed to by the receiver field in the inter-
preter.
Figure 15.1 D The internal representation for class Interpreter
struct interp_struct
int
int
struct
struct
object
object
object
object
object
object
uchar
};
{
t ref count;
t-size;/* should always be INTERPSIZE */
interp struct *creator;
interp-struct *sender;
*bytecodes;
*receiver;
*literals;
*context;
*stack;
* *stacktop;
* currentbyte;
178 The Implementation
\
,'i:.
Figure 15.2 0 CurrentByte points into the bytecode array
bytecode Bytecode Array
,
currentbyte
"" .I
The interface to the interpreter module is shown in Figure 15.3. The
procedure cr interpreter( ) creates a new instance of class Interpreter,
initializing it with the values given by the arguments. The bytecode pointer
is set to the first byecode in the method, and the size of the stack is
determined by examining the class of the receiver.
The routine copy arguments( ) is used to copy an array of argument
values into the context for an interpreter. It is used both by the courier
when the interpreter is first defined, and by the biock execution module
to pass arguments to interpreters associated with instances of class Block.
When a message returns, the response to the message must be passed
back to the sender. This is accomplished by pushing the response onto the
senders stack and then resuming execution of the sender interpreter. The
routine push object() performs the first of these actions, pushing an object
onto the staCk of an interpreter, both of which are passed as arguments.
Normally the interpreter involved is the sender although in the case of a
block returnit will be the sender of the creator.
The actual interpretation of bytecodes is performed by the process
resume( ). The procedure is so called because, when called by the process
manager, it resumes execution fromthe point it last left off. It then executes
bytecodes until either the method terminates or until a message is sent.
In the former case the interpreter is taken off the process manager queue
and the sender process moves to the top from where it will be subsequently
resumed by the process manager. In the second case the courier is called.
The courier then finds a method to match with a message, creates a new
instance of class Interpreter, and places the new interpreter in the front
of the sender interpreter in the process queue.
"\
.:l:.
The Interpreter
Figure 15.3 0 The interface to the interpreter module
cr interpreter(sender, receiver, literals, bitearray,context)
- struct interp_struct *sender;
struct obj struct *Iiterals, *bitearray, *receiver, *context;
creates a new instance of class Interpreter
179
copy arguments(anlnterpreter, argLocation, argCount, argArray)
struct interp_struct *anlnterpreter;
int argLocation, argCount;
object **argArray; .
takes a pointer to an array of arguments, and loads the argu-
ments into the context for the interpreter at the specified loca-
tions .
p u s h ~ object(anlnterpreter, anObject)
struct interp_struct *anlnterpreter;
struct obj struct *anObject;
pushes the object onto the stack associated with the interpreter
resume(anInterpreter)
struct interp struct * anInterpreter;
resumes (or begins) evaluation of the bytecodes associated with
an instance of class Interpreter
The structure of the procedure resume( ) is very regular. It is merely
a large infinite loop (exited by a return from within the loop) surrounding
a switch statement (Figure 15.4). The loop reads each bytecode in turn,
and the switch selects which actions to take to execute the opcode. The
macro nextbyte places the next byte in the argument and advances the
currentbyte pointer. By modular arithmetic the byte is then converted into
the high order and low order four-bit portions.
We can divide the bytecodes into groups with similar functions. These
groups are those that push objects on the stack (opcodes 1 to 5), pop an
object from the stack (opcodes 6 and 7), send a message (opcodes 8 through
13), block creation (opcode 14), and special instructions (opcode 15).
Push Opcodes
Writing the code for those actions that manipulate the stack is greatly
simplified by a number of macros that select various fields from the in-
terpreter object:
\
~ \ . -
180 The Implementation
Figure 15.4 0 The structure of the procedure resumer )
resume(anInterpreter)
struct interp struct *anlnterpreter
{ local dedarations
int highBits, lowBits;
while(l) {
nextbyte(hi9hBits);
lowBits = highBits % 16;
highBits / = 16;
switch(highBits) {
default:
cant_happen(9);
caseD:
actions for opcode 0
break;
case 15:
actions for opcode 15
break;
}
}
}
# define push(x) {assign(*(anlnterpreter->stacktop), x); '"
anlnterpreter->stacktop + + ;}
# define instvar(x) (anlnterpreter->receiver)->inst var[ x ]
# define tempvar(x) (anlnterpreter->context)->inst var[ x]
# define lit(x) (anInterpreter->Iiterals)->inst_var[ x]
These macros make the code for the first three opcodes trivial:
case 1: /*push instance variable */
push(i nstvar(lowBits» ;
break;
case 2: /* push context value */
push(tempvar(lowBits» ;
break;
The Interpreter
\
):. -
181
\
~ \ . -
case 3: /* push a literal */
push(1it(lowBits» ;
break;
Opcode 4, which pushes a class object onto the stack, is complicated
slightly by the fact that the object in the value field is a symbol, and first
the associated class object must be found. This is achieved by calling the
primitive manager with the message FINDCLASS. A later section will de-
scribe the primitive manager in more detail.
case /* push class */
tempobj = lit(lowBits);
if (! is symbol(tempobj» cant happen(9);
tempobj = primitive(FINDCLASS, 1, &tempobj);
push(tempobj) ;
break;
Opcode 5 selects either an integer, a pseudo variable, or a class and
pushes it onto the stack. The routine new_int returns an object of class
Integer, sharing multiple copies if the object occurs more than once. Each
of the pseudo variables has an internal C pointer associated with it. Oth-
erwise, to get a class a new symbol is created, and the primitive manager
is called as for opcode 4.
case 5: /* special literals */
if (IowBits < 10)
tempobj = new int(lowBits);
else if (IowBits = =-10)
tempobj = new int( -1 );
else if (lowBits = = 11)
tempobj = 0 true;
else if (lowBits =- = 12)
tempobj = 0 false;
else if (IowBits =- = 13)
tempobj = 0 nil;
else if (IowBits =- = 14)
tempobj = 0 smalltalk;
else if (IowBits =- = 15)
tempobj = (object *) runningProcess;
else if ((IowBits > = 30) && (IowBits < 60» {
1* get class */
tempobj = new sym(c1asspecial[lowBits - 30]);
tempobj = primTtive(FINDCLASSS, 1, &tempobj);
}
else tempobj = new int(lowBits);
push(tempobj); -
break;
182
\.
\,
"\
\
.'
~ \
:'
The Implementation
PopOpcodes
Like the push opcodes, the writing of the code for the instructions that
pop objects from the stack is greatly simplified by first defining useful
macros, in this case a macro to pop an object off the stack and return it:
# define popstack() (*(--anlnterpreter- >stacktop»
This makes Opcodes 6 and 7 easy.
case 6: /* pop and store instance variable */
assign(i nstvar(lowBits), popstack( »;
break;
case 7: /* pop and store in context */
assign(tempvar(lowBits), popstack( »;
break;
Message-Sending Opcodes
To send a message will usually require several instructions in bytecode.
First, the receiver for the message is pushed on the stack, followed by the
arguments in order. Finally, a send message instruction is given. When
the send message opcode is read, therefore, the stack looks as follows:
top of stack
argument
n
argument
1
receiver
bottom of stack
'.
.\.
The Interpreter 183
In the most general case, opcodes 8 and 9, the low-order bits of the
opcode give the number of arguments associated with the message. The
next byte is then a pointer into the literal table where a symbol corre-
sponding to the message is stored. The code for opcodes 8 and 9 is shared
by first placing the receiver into a local variable and executing an uncon-
ditional jump to a common section of code.
2
case 8; /* send a.message */
numargs = 10wBits;
nextbyte(i);
tempobj = lit(i);
if (! is symbol(tempobj» cant happen(9);
message = symbol value(tempobj);
goto do~ s e n d ; -
case 9: /* send a message to super */
numargs = 10wBits;
nextbyte(i);
tempobj = lit(i);
if (! is symbol(tempobj» cant happen(9);
message = symbol value(tempobj);
receiver = fnd super(anInterpreter - > receiver) ;
goto do_sendi";
/* do send - call courier to send a message */
do send:
receiver = *(anlnterpreter->stacktop - (numargs + 1»;
do send2:
- decstack(numargs + 1);
send mess(anlnterpreter, receiver, message, an Inter-
preter- >stacktop , numargs);
return;
2. The use of the goto in this case might be considered with horror by those who do
not understand the principles of structured programming. The closer one gets to an actual
machine, the greater the necessity for unconditional jumps becomes. (Think how difficult it
would be to do assembly language programming without jumps.) Most user programs, for-
tunately, do not have to be described at this level, and thus the avoidance of gotos usually
results in programs that are cleaner and easier to understand. Writing a virtual machine,
such as the interpreter, is in many ways similar to writing for an actual machine. In this
case, the sin of the "unstructured" goto seems less serious than the problems that could
arise from duplicatirig the code and thereby running the risk of doingtwo different things where
only one is intended, or from placing the duplicated code in a procedure since the amount
of sharing between the interpreter and the procedure would have to be so great. (This is, in
fact, one of those rare occasions when hue block-structured subprocedures would be useful
in C since so much information must be shared by the two routines.)
\ ", \ \
~ ~
184 The Implementation
The local variable numargs holds the number of arguments for the
message. The variable receiver contains the receiver for the message, a ~ d
the variable message contains the character string representing the message
name. (Note this is a pointer to a character sting and not an object.) The
code at label do send: looks into the stack to find the receiver; whereas,
in the case of opcode 9, the receiver is taken· to be the superobject of the
current receiver.
The courier is then called via the procedure send mess(). The courier
creates a new interpreter and places it in front of the current interpreter
in the process queue. Upon returning from the courier, the stack is dec-
remented and pointers in the stack are changed to point to nil. (This insures
that objects no longer being used are quickly recovered, instead of having
useless references to them left lying around). Control is then passed back
to the process manager. When the current process is restarted, the inter-
preter given control will be the new one placed in front of the present
interpreter by the courier.
Opcodes 10 through 13 avoid looking up the message, taking it instead
from a built-in table of messages.
case 10: /* send a special unary message */
numargs = 0;
message = unspecial[lowBits];
goto do_send;
case 11: /* send a special binary message */
numargs = 1;
message = binspecial[lowBits];
goto do_send;
case 13: /* send a special ternary keyword message */
numargs = 2;
message = keyspecial[lowBits];
goto do_send;
Opcode 12 could be handled similarly. However, by far the greatest
number of these messages sent and a sizai:>le percentage of all messages
sent, involve arguments that are both integers. One completely transparent
optimization, therefore, and a very cost-effective one, is to perform these
operations in the interpreter if the arguments are both instances of class
Integer. If not, then the standard calling sequence is followed. The macro
decstack( ) merely pops the specified number of locations from the stack.
case 12: /* send a special arithmetic message */
tempobj = *(anlnterpreter - >stacktop - 2);
if (! is_integer(tempobj)) goto ohwell;
i = int value(tempobj);
tempobJ = *(anlnterpreter- >stacktop - 1);
if (! is_integer(tempobj)) goto ohwell;
\
\ ~ .
"\
~ \ ~ \
"
-
-
Block Creation
The Interpreter
j = int value(tempobj);
decstack(2);
switch(lowBits) {
case 0: i + = j; break;
case 1: i - = j; break;
cQse 2: i * = j; break;
case 3: if (i < 0) i = -i;
i % = j; break;
case 4: if G< 0) i > > = (-j);
else i < < = j; break;
case 5: i & = j; break;
case 6: i I = j; break;
case 7: i = (i < j); break;
case 8: i = (i < = j); break;
case 9: i = (i = = j); break;
case 10: i = (i ! = j); break;
case 11: i = (i > j); break;
case 12: i = (i > j); break;
case 13: i % = j; break;
case 14: i / = j; break;
case 15: i =; (i <j) ? i : j;
break;
case 16: i = (i < j) ? j : i;
break;
default: cant happen(9);
} -
if «lowBits < 7) 11 (IowBits > 12»
tempobj = new int(i);
else tempobj = (i ? 0 true: 0 false);
push(tempobj); - -
break;
ohwell: /* oh well, send message conventional way */
numargs = 1;
message = arithspecial[lowBits];
goto do send;
185
In the bytecode format, the low-order bits of the block creation instruction
give the number of arguments to the block. If the number is non-zero, the
next byte gives the location in the context where the arguments should be
stored when the block is invoked. The byte following then gives the size
in bytes of the bytecodes containing the statements for the block. The
186 The Implementation
actual bytecodes for the statements in the block follow immediately the
block creation instruction.
The procedure new block() is called to create a block. A later section
will describe this in more detail. For now, it is sufficient to say that it
creates and initializes an instance of class Block, which is then pushed
onto the stack. The current bytecode pointer is then advanced over the
text of the block by using the macro skip( ).
case 14: 1* block creation */
numargs = lowBits;
if (numargs)
nextbyte(arglocation);
nextbyte(i); /* size of block */
push(new block(anInterpreter, numargs, arglocation» ;
skip(i); -
break;
Special Instructions
The code for opcode 15 is the largest of all the opcode sections because
there are so many different cases to be handled. Nonetheless, once the
macros used in the previous instructions have been defined, the code is
rather tediously simple. The two exceptions fo this 'are the code for those
instructions that return an object and the code to handle the primitive
instructions. There are three instructions that return an object. In the first,
Opcode (15.3) returns the object currently on the top of the stack, opcode
(15.4) performs a block return (which also takes its argument from the
top of the stack), and opcode (15.5) returns the receivec Block returns will
be described in a later section. The other two place the object to be returned
in the local variable tempobj, and then branches to a common return
section.
case 15: /* special bytecodes */
switch(lowBits) {
case 0: /* no-op */
break;
case 1: /* duplicate top of stack */
push(*(anlnterpreter->stacktop) - 1»;
break;
case 2: /* pop top of stack */
assign(*(anInterpreter->stacktop),0_ntl);
\
~ . - } . -
The Interpreter
ani nterpreter- >stacktop--;
break;
case :3: /* return top of stack */
tempobj = popstack():
goto do_return;
case 4: /* block return */
block_return(anInterpreter, popstack( »;
return;
case 5: /* self return */
tempobj = tempvar(O);
goto do_return;
case 6: /* skip on true */
nextbyte(i) ;
tempobj = popstack();
if (tempobj = = 0_true) {
skip(i);
push(o_nil);
}
break;
case 7; /* skip on false */
nextbyte(i);
tempobj = popstack();
if (tempobj = = o_false) {
skip(i);
push(o_nil);
}
break;
case 8: /* skip forward */
nextbyte(i);
skip(i);
break;
case 9: /* skip backward */
nextbyte(i);
skip( - i );
break;
case 10: /* execute a primitive */
nextbyte(numargs);
187
188
\
S. _
. The Implementation
nextbyte(i); /* primitive number */
decstack(numargs);
tempobj = pri mitive(i, numargs, anInterpreter - >stacktop);
push(tempobj) ;
break;
case 11: /* skip true, push true */
nextbyte(i);
tempobj = popstack();
if (tempobj = = 0 true) {
skip(i); -
anlnterpreter- >stacktop +
}
break;
+,
,
case 12: /* skip ·on false, push false */
nextbyte(i);
tempobj = popstack( );
if (tempobj = = 0 false) {
skip(i); -
anlnterpreter- >stacktop + + ;
}
break;
default:
cant_happen(9);
}
break;
/* do return-return from a message */
do return:
-sender = anlnterpreter - >sender;
if (is interpreter(sender» {
if (! is driver(sender»
push object(sender, tempobj);
link to process(sender);
} - -
else {
terminate process(runningProcess) ;
}
return;
The code at do return first checks to see if there is a sender. If there
is, and if the senderis not the driver, the object in tempobj is pushed onto
the stack in the sender. The sender is then made the first interpreter in
\
The Interpreter 189
-
-
-
the process queue by calling the procedure link to process( ). If there is
no sender, the current process is terminated. - -
The code to execute a primitive (case 10), first decrements the stack
over the arguments. It then calls the primitive handler, passing it the
primitive number, number of arguments, and a pointer to the location in
. the stack where the arguments (which have not been overwritten) are to
be found.
The Courier
The courier is so called because it carries a message, determines to whom
it should be sent and how it will be transmitted, but does not itself read
the messages. The interface to the courier is through the procedure
send mess(), which we have already described. Once called, the courier
walks up the super-object chain of the receiver, examining the classes of
each object in tum. In each class it looks at the list of messages to which
the class will respond, searching for one that will match the message being
sent.
If it finds a class that will respond to the message, it creates a context
for the message (the size of the context can be determined by the class
description) and an interpreter for the message. Calling the process man-
ager, the new interpreter is then linked to the head of the interpreter chain
for the currently running process. The courier afterwards returns to the
interpreter, which immediately returns back to the process manager. When
the process manager again restarts the process, the new interpreter will
be resumed.
If the courier cannot find a class that will respond to the message, it
produces an error message and a trace of the previous messages sent in
the current process. This trace is easily constructed by following back the
interpreter chain from the current interpreter back to an interpreter with
no sender, which must be the start of the interpreter chain. The pseudo
variable nil is then pushed onto the stack of the calling interpreter, which
is then restarted. Unfortunately, nil is seldom an appropriate value to be
used in this circumstance, and such errors have an annoying way of cas-
cading.
-
= The Primitive Handler
The primitive handler is the interface between the Smalltalk world and
the world of the underlying virtual machine. Any operation that cannot
be specified in Smalltalk, such as adding two floating point values together,
\
'\ ':.
'\
~ \ .'
190
Blocks
The Implementation
concatenating a pair of strings, or converting an integer into a floating point
value, must ultimately be performed via the primitive handler. In principle
only the primitive handler has detailed knowledge of the internal repre-
sentations of special objects and can manipulate the various fields in these
objects.
3
The structure of the primitive handler (Figure 15.5) is rather compli-
cated but very regular. This complex structure is dictated by the necessity
to combine some common operations to reduce the size of the primitive
handler as much as possible. (It is already the largest module in the Little
Smalltalk system) Appendix 4 will show that primitive operations seem
to be collected in groups of ten. For example binary integer operations
have numbers between 10and 29, unary integer operations have numbers
between 30 and 39, character operations numbers between 40 and 49, and
so on. Dividing the primitive number by ten will tell which group the
primitive falls into. From this information you can check type to insure
the arguments to the primitive are correct (for example, that character
primitives are indeed presented with character arguments) can be per-
formed. Also the internal values (for example, the integer values from
instances of class Integer) can be placed into local variables within the
primitive handler. Thus the start of the primitive handler is a large switch
statement to perform type checking.
Once type checking has been performed, a several-page switch state-
ment is used to find the appropriate action for each primitive type. After
performing the correct actions, the primitive handler will return an object.
This object can be of any type (Symbol, String, Float, etc.), and once
more an attempt is made to combine similar actions so as to reduce the
necessity for duplicating code. Each of the individual code sections for the
various primitive operations ends by an unconditional jump to a return
section of the appropriate type.
The routine new block(), introduced in the discussion of opcode 14, cre-
ates a new instance of class Block. Instances of Block are special objects
with an internal structure as shown in Figure 15.6. The interpreter field
in each block points to a copy of the interpreter in which it was created.
3. In practice this is not quite hue, as the memory manager must also know about the
internal structure of all objects. Also some complex operations are handled by special routines
in the modules for different types of objects, such as symbols or classes. These are-called by
the primitive handler, however, and logically can be considered to be part of the primitive
handler module.
\
s.
The Interpreter
Figure 15.5 D The structure of the primitive handler
191
\
~ '} .
perform
action
type
check
return
character
return
float
return
symbol
\
:.....
192 The Implementation
Figure 15.6 0 The structure of instances of class Block
struct block struct {
int - b ref count;
int b-size;
struct interp struct*b interpreter;
int b numargs;
int b-arglocation;
} ; -
The copy shares everything with the original except for the stack and the
currentbyte pointer.
In response to a value message, instances of class Block execute the
BlockExecute primitive. This instruction, after verifying that the number
of arguments matches the number of arguments defined by the block, again
copies the interpreter for the block, copies the arguments into the context
for the interpreter, and appends the interpreter to the front of the inter-
preter chain for the current process.
Returning a value from a block (in the absence of an explicit block
return) therefore requires exactly the same sequence of events as a normal
return. In fact, the class parser and the command line driver generate
bytecodes so that the same mechanism is used.
In order to perform a block return, the interpreter chain is examined,
searching for the creating interpreter. If found, the same actions are taken
as would if the creator itself were returning the object being returned by
the block. The creator, and all interpreters following it, will be removed
from the interpreter chain, and the sender of the creator will move to the
top to be resumed next by the process manager.
If the creator is not found in the interpreter chain (if a block containing
a return is placed into a variable or returned from a message and thus
outlives its creator), an error message is produced and the value that would
have been returned is returned to the sender of the value message which
invoked the block.
\
,\.
References
Abelson, H., and diSessa, A. [1981] Turtle Geometry: The Computer as a
Medium for Exploring Mathematics. Boston: MIT Press.
Almes, G. T.; Black, A. P.; Lazowska, E. D.; and Noe, J. D. [1985] "The
Eden System: A Technical Review." IEEE Transactions on Software
Engineering, SE-11: 43-59.
Presents an overview of a modem object-oriented operating sys-
tem.
Birtwistle, G. M.; Dahl, Q.-J.; Myhrhaug, B.; and Nygaard, K. [1973] Simula
Begin. Lund, Sweden: Studentlitteratur.
Simula is a language of the Algol family designed for simulation
and is a very important ancestor of Smalltalk. The concept of
Classes was inherited from Simula.
Birtwistle, G. M. [1979] DEMOS; A System for Discrete Event Modelling on
Simula. London: MacMillan.
A comprehensive description of how the language Simula can be
used in producing discrete event models of the type described in
Chapter 7.
Budd, T. A. [1982] "An Implementation of Generators in C." Computer
Languages, 7: 69-88.
Describes how a simple form of generators can be implemented
in the language C.
Byte [1981] Special Issue on Smalltalk 6: 14-378.
A special issue of the programming magazine Byte containing a
large number of articles on Smalltalk-80 written by members of
the Xerox Learning Research Group.
Campbell, J. A., (ed.) [1984] Implementations ofPROLOG. NewYork: Wiley
& Sons.
PROLOG is a language for logic programming. This collection of
papers describes many different aspects of the implementation of
the language.
Dahl, O.-J.; Dijkstra, E.; and Hoare, A. [1972] Structured Programming.
London: Academic Press.
Introduced the notion of structured programming. Includes an
article by O-J Dahl on the language Simula.
Dijkstra, E. W. [1965] "Cooperating Sequential Processes." Technical
Report EWD-123. Eindhoven, the Netherlands: Technological Univer-
sity.
193
194 References
An early paper on sychronization, describes the "Dining Philoso-
phers" problem discussed in Chapter 10.
Ghezzi, C., and Jazayeri, M. [1982] Progranlming Language Concepts, New
York: Wiley & Sons.
Describes the features typically found in languages of the ALGOL
family and their conventional implementations.
Gibbs, G. I., ed. [1974] Handbook of Gaines and Shnulation Exercise. Bev-
erly Hills, Cal.: Sage Publications, Inc.
Presents· references to a large number of games and simulation
exercises.
Goldberg, A., ed., and Kay, A., ed. [1976] Sl1wlltalk-72 Instruction Manual,
Xerox PARC Technical Report
Describes the language Smalltalk-72, one of the first in the evo-
lution of Smalltalk languages.
Goldberg, A., and Robson, D. [1983] Smalltalk-80: The Language and Its
.Implementation. Reading, Mass.: Addison-Wesley.
. The definitive description of Smalltalk. Contains many extensive
examples of simulations and use of the graphics features of the
Smalltalk-80 language.
Goldberg, A. [1983] Smalltalk-80: The Interactive Programming Environ-
ment. Reading, Mass.: Addison-Wesley.
The Smalltalk-80 Programming system developed at Xerox Pare is
much more than just the Smalltalk language. This book describes
features of the programming environment developed for Small-
talk-80.
Greenberg, S. [1972] GPSS Primer. New York: Wiley & Sons.
An introduction to the computer simulation language GPSS.
Griswold, R. E.; Poage, J. F.; and Polonsky, I. P. [1971] The SNOBOL4
Programming Language. Englewood Cliffs, N. J.: Prentice-Hall.
Snobol4 is one of the earliest attempts at a language for nonnu-
meric programming.
Griswold, R. E., and Griswold, M. T. [1983] The Icon Progrmnming Lan-
guage. Englewood Cliffs, New Jersey: Prentice-Hall.
Icon is a language for nonnumerical problems and a descendant
of Snobol4 (Griswold 71). Many of the ideas concerning generators
described in Chapter 8 were derived from Icon.
Griswold, R. E., and O'Bagy, J. [1985] "Seque: A Language for Program-
ming with Streams." TR 85-2. Tucson, Arizona: The University of Ar-
izona Department of Computer Science.
The language Seque is derived from Icon (Griswold 83) and at-
tempts to deal with sequences as a formal object, rather than with
generators.
References 195
Hanson, D. R.; and Griswold, R. E. [1978] "The SLS Procedure Mecha-
nism." Comlnunications of the ACM, 21: 392-400.
The programming language SLS provides a great deal of flexibility
in the area of procedure activation and parameter passing. The
concept of filters, described in Chapter 8, is taken from SLS.
Hewitt, C.; Bishop, P.; and Steiger, R. [1973] "A Universal Modular Actor
Formalism for Artificial Intelligence." Proceedings of the 3rd Interna-
tional Joint Conference on. Artificial Intelligence.
Actors is a technique for describing object-oriented programming
in Lisp. .
Ingalls, D. H. [1978] "The Smalltalk-76 Programming System: Design and
Implementation." Proceedings of the Fifth Principles of Programming
Languages Sylnposium, January 1978: 9-16.
The language Smalltalk-76 was the immediate predecessor to the
language Smalltalk-80 on which Little Smalltalk is based.
Kay, A. [1969] "The Reactive Engine" Ph.D. Thesis, University of Utah.
(available on University Microfilms).
Describes the Flex system, an important predecessor of Smalltalk.
Kay, A. [1977] "Microelectronics and the Personal Computer." Scientific
Alnerican, 237: 230-244.
A good introduction to the philosophy behind the development of
the Smalltalk-80 programming system. Describes some early ex-
periments involving teaching Smalltalk to children.
Knuth, D. [1981] The Art of Computer Progrmnlning. Fundamental Algo-
rithms, Vol. 1; Seminumerical Algorithms, VoL 2; Sorting and Search-
ing, VoL 3. Reading, Mass: Addison-Wesley.
These three volumes (the first of a planned seven-volume collec-
tion) present an extremely complete analysis of most of the im-
portant algorithms used in computer science.
Krasner, G., ed. [1983] Smalltalk-BO Bits ofHistory, Words ofAdvice. Read-
ing, Mass.: Addison-Wesley.
A collection of papers describing various aspects of the implemen-
tation of the Smalltalk-80 system.
Koved, L. [1984] "The Object Model: A Historical Perspective." Technical
Report TR-1443. College Park, Md.: The University of Maryland De-
partment of Computer Sciences, September 1984.
Describes how the object-oriented model has influenced machine
architecture, operating systems, and language design. Includes a
lengthy reference list.
LaLonde, W. R.; Thomas, D. A.; and Pugh, J. R. [1984] "Teaching Fifth
Generation Computing: The Importance of Smalltalk." Technical
Report SCS-TR-64. Ottawa, Ontario: Carleton University School of
Computer Science, October 1984.
196
\
'.
, ~ .
References
Argues that the Smalltalk language will be as important as Prolog
in developing fifth-generation computer systems. Includes a
lengthy reference list of associated literature.
Liskov, B.; Atkinson, R.; Bloom, T.; Moss, E.; Schaffert, J. C.; Scheifler, R.;
and Snyder, A. [1981] CLU Reference Manual. New York: Springer-
Verlag.
CLU is a modern language in the Algol family. Although the lan-
guage includes a concept called generators, they are considerably
different from generators in Little Smalltalk.
Maryanski, F. [1980] Digital Computer Simulation. Rochelle Park, N.J.:
Hayden Book Company, Inc.
A rather general introduction to computer simulation models il-
lustrated with examples from the languages GPSS, Simscript,
CSMP, and Dynamo.
Ord-Smith, R. K., and Stephenson, J. [1975] Computer Simulation ofCon-
tinuous Systems. Cambridge, England: Cambridge University Press.
Papert, S. [1980] MindStorms: Children, Computers and Powerful Ideas.
City: Basic Books.
Introduces the language LOGO.
Peterson, J., and Silberschatz, A. [1983] Operating System Concepts. Read-
ing, Mass.: Addison-Wesley.
Agood introductory operating systems textbook. Discusses various
solutions to the "Dining Philosophers" problem discussed in Chap-
ter 10.
Pinnow, K. W.; Ranweiler, J. G.; and Miller, J. F. [1982] "The IBM Sys-
tem/38 Object-Oriented Architecture" in Computer Structures: Princi-
ples and Examples. pp 537-540. New York: McGraw-Hill.
Describes some of the object-oriented features of a modem pro-
cessor and its associated operating system.
Rattner, J., and Cox, G. [1980] "Object-Based Computer Architecture."
. Computer Architecture News 8: 4-11.
Describes the influence of the object-orientedviewpoint on machine
architecture.
Reynolds, C. W. [1982] "Computer Animation with Scripts and Actors."
Computer Graphics 16: 289-296.
Describes how object-oriented techniques (the Actor model) can
be used for computer animation.
Shaw, M. [1980] "The Impact of Abstraction Concerns in Modern Pro-
gramming Languages." Proceedings of the IEEE 68: 1119-1130.
Describes abstraction techniques for several modern languages.
Shaw, M. ed. [1981] Alphard: Fonn and Content. New York: Springer-
Verlag.
References 197
Acollection of papers on the language Alphard, a modern language
in the Algol family
Smith, D. C., .and Enea, H. K. [1973] "Backtracking in MLISP2." Proceed-
ings of the 3rd International Joint Conference on Artificial Intelligence,
August 1973: 677-685.
Weinreb, D., and Moon, D. [1980] "Flavors: Message Passing in the Lisp
Machine." MIT AI Mento Nwnber 602, November 1980.
Describes a technique for adding the ability to represent objects
and message passing to the computer language LISP.
Wulf, W. A.; Cohen, E.; Corwin, W.; Jones, A.; Levin, R.; Pierson, C.; and
Pollack, F. [1974] "HYDRA: The Kernel of a Multiprocessor Operating
System." Contntunications of the ACM, June 1974, pp. 337-345.
Describes the HYDRA operating system, which is based on objects
communicating via messages.
Zeigler, B. P. [1976] Theory of Modelling and Sintulation. New York: Wiley
& Sons
A rather theoretical overview of simulation methods.
Projects
This section contains a series of projects suitable for graduate or advanced
undergraduate students in a one-term course based on the material in this
book. Some of the projects involve working only in Smalltalk and therefore
can be attempted by students with knowledge only of the first part of the
book. Other projects involve making modifications to the actual imple-
mentation and therefore require knowledge of the second half of the book.
1. Card Games
Instances of the following class when properly initialized can be used to
represent single playing cards from a conventional deck of cards.
Class Card
I suit face I
[
suit: suitValue face: face Value
suit ~ suitValue
face ~ faceValue
printString I print I
Switch new: face;
case: 1 do: [ print ~ 'ace' J ;
case: 10 do: [ pri nt ~ 'jack' J ;
case: 11 do: [ print ~ 'queen' ] ;
case: 12 do: [ print ~ 'king' J ;
default: [ print ~ face printString J .
print ~ print, ' of ' ,
( #('hearts' 'clubs' 'diamonds' 'spades') at: suit)
t print
Implement the class Deck, which represents a deck of playing cards.
Instances of Deck respond to the following messages:
shuffle The deck of cards is shuffled into random order. The order can
ei ther be determined a priori in response to this message or
produced as each card is dealt out.
deal One card is dealt from the deck and is not replaced. That is,
once a card is dealt from the deck it cannot be dealt again until
after the deck has been shuffled again.
198
Projects 199
deal: As many cards as indicated by the argument are dealt out. Deal
returns an array of cards.
Using Deck, devise a simulation for a simple card game such as Sol-
itaire or Blackjack. You may wish to add further messages to class Card
or to make it a subclass of Magnitude.
~ . Arbitrary Precision Arithmetic
Implement the class BigInteger (subclass of Number). Instances of class
Biglnteger represent integers of arbitrary size. Internally, integers larger
than can be accommodated in the underlying machine representation are
encoded as an array of values. For example, suppose only values less than
100 could be represented in machine words. A larger value, say 1476632,
could be represented by the array #( 1 47 66 32 ). (Actually, depending
upon the algorithms selected, it may be preferable to keep the values in
reverse order).
Instances of class Biglnteger should respond to the following mes-
sages:
coerce: The argument should be an instance of class Integer. Return
a Biglnteger with the same magnitude.
+ If the argument is a BigInteger, return a new BigInteger rep-
resenting the sum. If the argument is not a Biglnteger, pass
the message up to the superclass (Number). Similar mes-
sages for -, *, < 1 = and < = .
printString Return a string representation of the integer value.
Other messages may be necessary, depending upon your implemen-
tation.
It is suggested that you start with an easy approximation. For example,
produce a class that works only for positive numbers and the message +.
Later add negative values and other messages. (Volume 2 of (Knuth 81)
describes some algorithms that might be useful for this project.)
3. Polynomials
Implement the class Polynomial. Instances of Polynomial represent pol-
ynomial values with numerical coefficients. As with the last project, the
class Biglnteger, polynomial coefficients are maintained internally by an
array of coefficients. Instances of Polynomial should respond to the fol-
lowing messages:
coerce: Return a new polynomial of degree zero with the argument
as coefficient.
\
.0;.,.
200 Projects
degree Return the degree of the polynomial.
coefficient: Return the value of the named coefficient, or zero if no coef-
ficient matches the argument.
evaI : Return the numerical result produced by evaluating the pol-
ynomial on the argument value.
+ If the argument is a Polynomial, return a new Polynomial
representing the sum. If"the argument is not a Polynomial,
pass the message up to the superclass (Number). Similar
messages for -, *, <, = and < =.
printString Return a string representation of the polynomial value.
Other messages may be necessary, depending upon the implementa-
tion technique.
4. Matrices
The class Array provides protocol for vectors, that is, one-dimensional
arrays. Matrices of higher dimension can be thought of as being composed
of two vectors, a shape vector giving the dimension of the array and a
values vector maintaining the values in the array. For example, the two-
dimensional array
10
12
6
3
7
14
13
9
21
can be thought of as being composed of the following two vectors
shape #( 3 3 )
values #( 10 3 13 12 7 9.6 14 21 )
Notice the values are stored in row major, or ravel order.
Define a class for manipulating matrices of arbitrary dimension. Values
in the matrix can be defined either by giving them an explicit shape or
values array or by modifying a single element. What functionality should
matrices exhibit? Possibilities include pointwise multiplication or addition
by a scalar or by another matrix, matrix multiplication, inner or cross
products, or others.
5. APL
The programming language APL provides a rich repertoire of operations
that can be performed on multidimensional arrays. Because the class Ma-
trix was defined in the last project, many of the APL operations can be
easily simulated in Smalltalk (ignoring syntax, of course).
\ "\
\
\
~ ~
.,.
~ \ ~ \
Projects 201
6. Lisp
'The class List provides a simple list structure. Show how this class could
be modified to accept the LISP-like messages car, cdr, and cons. What
other classes would have to be changed? What other Lisp-like features
could be simulated?
7. Numerical Generality
One can envision a class Fraction representing rational values. Instances
of class Fraction would be represented internally by a pair of integers
representing a numerator and denominator. A fraction could be produced
by the division of two integers. Operations involving two fractions or a
fraction and an integer would produce a new fraction. Operations involving
a fraction and a floating point value would produce a floating point value.
One difficulty of implementing the class Fraction is the built-in notion
of numerical generality. The numerical generality hierarchy is Integer,
then Float, then any user-defined classes. In the case of Fraction, one
would like the generality to come between Integer and Float.
Investigate how numerical generality (primitive number 6) is imple-
mented. Suggest and implement an alternative scheme that permits nu-
merical generality to be assigned at run-time rather than having a built-
in hierarchy. Using this new scheme, implement the class Fraction.
8. Cursor Motions
On many terminals the cursor can be moved to an arbitrary location on
the screen by sending a special sequence of characters (usually a sequence
of unprintable characters). Note that a "character" can be constructed for
any nonzero value using an integer and the message asCharacter.
If you have a terminal with such a capability, describe how to modify
the class String to respond to the following message
aString printAt: aPoint
The point is taken to be a pair of coordinates, and the receiver string should
be printed at that location.
9. Bar Graphs
Instances of the class Bar Graph should respond to messages setting a
header and/or a footer and providing an array of values and, optionally,
an array of titles associated with each value. It should then produce a
graph, such as that shown below, where the number of stars printed is
proportional to the value of the given item.
202
\.
,'i:.
title1
title2
titlen
value1
value2
value2
Projects
header
*************
*********
******************
footer
turn:
down
go:
goto:
10. Function Plots
Describe a class, Plot, that will produce plots of functions. You may wish
. to augment your plot with headers, footers, or other informative indica-
tions as in the bar graphs just described. Instances of class Plot should
respond to either a message giving. an ordered collection of points or a
message giving the function to be graphed. For example, the following
might be a typical session:
graph ~ Plot new
graph from: - 10 to: 20
graph function: [:x I (3 * (x t 2» + ( 2 * x) + 5]
graph plot
11. Pens
This project assumes you have access to an output device on which you
can draw lines between pairs of coordinates. If you have such a device,
implement the class Pen, a tool for investigating "turtle geometry." (See
(Papert 80), (Abelson 81), and Goldberg 83); the name "turtle" describes
the drawing instrument in the Abelson system.) Instances of ciass Pen
represent a writing instrument on the terminal screen. Such instruments
have a state (either up, off the screen; or down, on the screen) and a
direction (between 0 and 211' radians).
Pens can be directed to move either a given distance (go:) or to a specific
location (goto:) If the pen is down, a line is drawn on the terminal screen.
If the pen is up, no drawing takes place.
Instances of class Pen should respond to the following messages:
location Return the location of the pen as a Point.
up Set the state of the pen to up. When up, a pen does not write
on the surface as it moves.
Set the state of the pen to down. When down, a pen writes on
the surface as it moves.
The argument is an amount in radians. The direction of the
pen is moved the indicated amount.
The argument gives the distance the pen should move.
The argument is a Point the pen should move to.
Projects 203
Other messages may suggest themselves to you. As an example, the
following script:
bic ~ Pen new
bic down
(3 to: 6) do: [:nsides Insides timesRepeat: [ go: 1 . bic turn: ( 2 pi /
nsides ) radians] ]
would produce the following picture:
12. Collections
The basic types of collections in Little Smalltalk are represented by classes
Bag, Set, Dictionary, List, and Array. There are various different schemes
to implement methods for these classes. Important considerations are the
speed with ·which objects can be inserted, tested, or removed from the
collection; the size (number of auxiliary objects required) to implement
an instance of the collection; and the ease with which the implementation
can be realized (for instance, the number of primitive operations required).
An attractive implementation technique is to make some classes "fund-
amental" in the sense that instances of the class may be represented by
special objects (chapter 11) and/or may use primitive operations to respond
to insertion or deletion messages. Other types of collections can then be
implemented by internally using instances of fundamental classes. Ex-
amples showing how this might be done are as follows:
A Set can be implemented using a Bag by merely checking to see if
each entry is already in the set before it is in serted.
An Array can be implemented, using a Dictionary with integer keys.
(Nothing is said about this being efficient, merely possible).
\
- ~ .
\
} ~ ~ . " , : , : ~ \
204
Projects
A Set or Bag can be implemented by a Dictionary. The element can
be used as the key for the dictionary and (in the case of Bag) the
value field used to store the number of times the element is re-
peated.
Conversely, a Dictionary can be implemented using a Set by storing
each key-value pair as an instance of class Point.
A Set (also a Dictionary) can be implemented using an Array and in-
stances of List. The array is used as a hash table (there is a primitive
to produce a hash value from any object) and each entry in the array
is a List containing all elements in the set that hash to the same
value.
Investigate these possibilities and any others you can think of. Compare
the number of operations involved to do insertions or deletions in the
various methods. If possible, implement the various techniques and pro-
duce actual timings comparing them. Examine the implementation of the
classes used in the Little Smalltalk system and explain the techniques used
there. Look at the implementations described in (Goldberg 83) and contrast
them with the techniques used in the Little Smalltalk system.
13. Opcode Design
Modify the parser so that it can be used to produce a static analysis of
bytecode frequency, including the frequency of each of the special opcodes
(opcode 15) and the frequency of each message corresponding to a built-
in value (opcodes 10-13). Using the altered parser, recompile each of the
classes in the standard prelude and tabulate the results. Are there any
surprises? Based on these figures, should primitives have been given their
own opcode? Which other opcode should be eliminated?
Modify the interpreter to measure the frequency of bytecode execution
while a program is running. Using the altered system, execute a large set
of programs (say the programs used to perform installation testing). How
closely do the dynamic figures match the static figures?
Modify the courier to tabulate the number of messages sent while
executing a given set of programs. What messages occur most frequently?
Is there any way execution could be optimized for these messages?
14. Windows
Windows is a technique for organizing a single physical terminal screen
into a larger number of different logical screens. Each window serves as
a separate entity and can display data independently of all other windows.
If you have access to a software system supporting windows (such as the
\
\
\ \
~ ~
.,.
~ \ ~ \
Projects 205
Curses package
l
or the Maryland Windows package
2
) implement the class
Window.
Instances of the class Window should respond to the following mes-
sages:
from aPoint to: aPoint
Specifies the upper left and lower right extent of the window.
put: aString
Places the string as output in the window, scrolling the window
up if necessary Gust as on a terminal).
moveToFront
May move the window, or other windows, so that the receiver
window is completely exposed (i.e., not covered by other win-
dows).
In addition, the class Smalltalk should be modified so that the pseudo
variable smalltalk will respond to the following messages:
smalltalk inputWindow: aWindow
Specifies that all future input should be taken from the specified
window. This will require modifications in several places inter-
nally to insure that the cursor is always sitting on the given win-
dow when input is expected.
smalltalk outputWindow: aWindow
Specifies that standard output (output produced via the print
message) should appear on the designated window.
In order to accomplish the implementation of this class, you may need
to create a special object for class Window (see Chapter 12). Since window
operations are produced by calling C routines, they will necessarily have
to be implemented via primitive calls, and thus the primitive handler will
have to be modified. You may also require many more internal messages.
15. Context Saving
When an object is no longer being referenced, that is, when its reference
count reaches zero, the storage for the object is reclaimed and the object
effectively disappears. Thus it is clear that every accessible object must be
pointed to by at least one other object. If this were uniformly true, all of
memory would be one large cycle. Fortunately, there are exceptions.
Three sets of objects can exist without being pointed to:
1. Kenneth Arnold, Screen Updating and Cursor Movement Optimization: A Library Pack-
age, Unix 4.1 distribution, Berkeley, California, 1981.
2. Mark Weiser, Chris Torek, and Richard J. WOQQ, Three Window Systems, University
of Maryland Technical Report TR-1444, 1985.
206
\.
Projects
1. The driver maintains a pair of arrays (var names and var values)
containing the names and values of all instance variables known at the
command level.
2. An internal dictionary (in file cldict.c) is maintained, recording the
objects representing each known class.
3. Pseudo variables (such as true and false) each have a corresponding
globar variable (0_true or 0 _false, for example) in the internal C uni-
verse.
Knowing that these are the only pointers into memory, develop an
algorithm that will save the current value of all known objects. Your al-
gorithm should produce a script of Smalltalk commands that when read
will recreate the state of the machine at the time the script was produced.
This may require adding one or more fields to the internal structure of
every object (although the fewer changes required, of course, the better).
Make sure your algorithm works properly in the case of multiple copies
of any object. You will probably have to make extensive use of the prim-
itives 111 and 112 that extract or set an arbitrary field in any object.
You may wish to start with a simpler case, such as not dealing with
classes or built-in objects.
16. Garbage Collection
The preceding project description explained how to access every object
along a path headed by one of a small number of starting locations. This
is one of the major requirements necessary in order to replace the current
reference-counting memory management system with a scheme using gar-
bage collection. Investigate at least one garbage collection algorithm and
describe what changes would be required to both the internal represen-
tation of objects and to the Little Smalltalk implementation to incorporate
this system. A description of simple garbage collection techniques is pre-
sented in (Ghezzi 82); schemes directed more specifically at Smalltalk are
given in (Krasner 83).
17. Processor Scheduling
Chapter 14 describes the Process manager. In the Little Smalltalk system
all processes have the same priority, and each new process is merely se-
lected in turn form a circular list. An alternative scheme would permit the
user to dynamically assign priorities to processes, with processing having
a higher priority because of executing more frequently.
Describe a workable priority scheme. Describe what changes would
have to be made to the Little Smalltalk system to implement your s c h e m ~ .
\
\ \. \
>
.'
.> . - ~
Projects 207
18. Inheritance of Variables
In the Smalltalk-80 programming system (the Smalltalk language avail-
able from Xerox) not only are methods inherited from a superclass, but
variables may be inherited as well. That is, suppose A is a subclass of B.
An instance variable used in B may also be accessed or modified by meth-
ods in class A.
Given that, when class A is parsed, class B may not exist or may be
modified later, devise a scheme to implement variable inheritance. (Hint,
define new special opcodes which, like the class opcode, point to a literal
value).
19. Multiple Inheritance
Multiple inheritance is a term used to describe a situation where an object
inherits methods from two or more super classes. An example will illustrate
this concept. In the standard classes for the Little Smalltalk system, the
class SequenceableCollection is rather artificially placed as a subclass of
KeyedCollection. As a result, the class List, which does not have keys, is
nevertheless a subclass of KeyedCollection. A better organization might
have been the following:
Collection
/ ~
? O I ~ ? I ~
Dictionary ArrayedCollection List
Here the classes SequenceableCollection and KeyedCollection are both
subclasses of Collection. The class List is sequenceable, but not keyed,
thus it is a subclass of SequenceableCollection. Similarly, the class Dic-
tionary is keyed, but not sequenceable, and is thus a subclass of
KeyedCollection. The class AriayedCollection, however, is both se-
quenceable and keyed, and thus instances of ArrayedCollection inherit'
from both the classes KeyedCollection and SequenceableCollection.
Chapter 12 described how inheritance was implemented both in the
structure of class objects (using a symbol representing the name of the
\ \
\
;;:.
> .~
).
... ,-'.
208 Projects
superclass) and in the internal representation of objects (using the super-
object pointer). One approach in both these instances is to use an array
of objects to represent the information about superclasses. In the class
object this would be an array of symbols indicating the super objects. In
each individual object this would be an array of superobjects.
Describe what effect this change would have on the internal structure
of the Little Smalltalk system. Show in detail how the courier could be
modified to determine the receiver of a message in the case of multiple
inheritance.
Appendix 1
Running Little Smalltalk
The Little Smalltalk system is invoked by typing the command st. The
systemis interactive -that is, the user types an expression at the keyboard,
and the system responds by evaluating the expression and typing the result.
For example, when the expression 3 + 4 is typed, the value 7 is displayed
on the output. Execution is terminated by typing control-D. A sample
execution session is shown in Figure 1.
Whenever the system is waiting for the user to type a command, the
cursor is slightly indented. Normally output appears immediately following
the command unless it is written to a file or redirected by a Unix directive.
Instance variables for the command level can be created by assigning
a value to a new variable name. Thereafter that variable can be used at
the command level although it is not known within the scope of any
method. The variable II lastII always contains the value returned by the last
expression typed. Figure 2 shows the creation of a variable. Note that the
assignment arrow is formed as two-character sequence.
The default behavior is for the value of expressions, with the exception
of assignments, to be typed automatically as they are evaluated. This be-
havior can be modified either by using the.-d flag (see below), or by passing
a message to the pseudo variable smantalk (see the description of the
class Smalltalk in Appendix 3).
Class descriptions must be read from files, they cannot be entered
interactively. Class descriptions are entered by using a system directive.
Figure 1 0 A sample Little Smalltalk session
% st
Little Smalltalk
3 + 4
7
A 0
%
209
\
,".0:.
210 Appendix/Running Little Smalltalk
Figure 2 0 Creating variables
newvar < - 2 I 3
newvar
0.666667
2 raisedTo:newvar + (4 I 3)
4
last
4
For example, to include a class description contained in a file named
newclass.st, the following system directive should be issued:
)i newclass.st
A list of files containing class descriptions can also be given as arguments
to the st command. The command
%st file
1
••• file
n
is equivalent to the sequence
%st
Little Smalltalk
)i file,
)i file
n
A table of system directives is given below.
)e filename Edit the named file. The Little Smalltalk system will sUs-
pend, leaving the user in an editor for making changes to
the named file. Upon leaving the editor, the named file will
automatically be included, as if the )i directive had been
typed.
)9 filename Search for an entry in the system library area matching the
filename. If it is found, the class descriptions in the library
entry are included. This command is useful for including
commonly-used classes that are not part of the standard
prelude. such as classes for statistics applications or graph-
ics. Directions for setting up library entries can be found in
the Little Smalltalk installation notes.
)i filename Include the named file. The file must contain one or more
class descriptions. The class descriptions are parsed, and if
they are syntactically legal, new instances of class Class are
added to the Smalltalk system.
\
;.
\
\
~ ~ ) ~ }
Appendix!Running Little Smalltalk
211
)1 filename Load a previously-saved environment from the named file.
The current values of all variables are overridden. The file
must have been created using the )s directive (below).
)r filename Read the named file. The file must contain Smalltalk state-
ments as they would be typed at the keyboard. The effect is
the same as if the lines of the file had been typed at the
keyboard. The file cannot contain class descriptions.
)s filename Save the current state in the named file. The values of all
variables are saved and can later be reloaded using the )1
directive (above).
)!string Execute the remainder of the line following the exclamation
point as a Unix command. Nothing is done with the output
of the command nor is the returning status of the command
recorded.
Note that the )e system directive invokes an editor on a file containing
class descriptions and then automatically includes the file when the editor
is exited. Classes also respond to the message edit, which will have the
same effect as the )e directive applied to the file containing the class de-
scription. Thus the typical debug/edit/debug cycle involves repeated uses
of the)e directive or the edit message until a desired outcome is achieved.
The editor invoked by the )e directive can be changed by setting the ED-
ITOR variable in the user's environment.
The st command can be followed by any of the following options:
-a If the -a option is given, statistics on the number of memory al-
locations will be displayed following execution.
-ddigit
If the digit is zero, only those results explicitly requested by the
user will be printed. If 1, the values of expressions typed at the
keyboard will be displayed (this is the default). If 2, the values of
expressions and the values assigned in assignment statements will
be displayed.
-f The -f option indicates that fast loading should be used, it loads
a binary save image for the standard library.
-9 The next argument is taken to be the name of an additional library
stored in the system library area. The library is loaded following
the standard prelude, as if a ll)g" directive were given at the be-
ginning of execution.
-I The next argument is taken to be the name of a file containing a
binary image saved using the )s directive. This binary image is
loaded prior to execution.
-m Do not perform fast loading. (Used when fastloading is the default.)
-n The -n option, if given, suppresses the loading of the standard
212 Appendix/Running Little Smalltalk
library. Since this gives you a system with almost no functionality,
it is seldom useful except during debugging.
-r The next argument is taken to be the name of a file of Smalltalk
commands. The file is included prior to execution, as if a ")r" di-
rective were given at the beginning of execution.
-s In normal operation, the number of reference count increments and
decrements is printed at the end of execution just prior to exit. In
the absence of cycles, these increments should equal decrements.
Since cycles can cause large chunks of memory to become unreach-
able and seriously degrade performance, this information is often
useful in debugging. The -s option, if given, suppresses the print-
ing of this information.
After the options, you can list any number of files. The files, if given,
must contain class descriptions. Appendix 2 gives the syntax for class
descriptions. Any classes so defined are included along with the standard
library of classes before execution begins.
-
-
-
"\
~ \ . -
Appendix 2
Syntax Charts
Syntax charts for the language accepted by the Little Smalltalk system are
described on the following pages. The following is a sample class descrip-
tion:
Class Set :Collection
I diet I
[
new
diet < - Dietionary new
add: newElement
diet at: newElement
ifAbsent: [diet at: newElement put: 1]
remove: oldElement ifAbsent: exceptionBlock
diet removeKey: oldElement ifAbsent: exceptionBlock
size
i diet size
occurencesOf: anElement
i diet at: anElement ifAbsent: [0]
first
diet first.
i diet currentKey
next
diet next.
i diet currentKey
213
214 Appendix 2 Syntax Charts
Class Description
Class Heading
The keyword Class must begin with an uppercase letter and consist
of lowercase letters, as shown.
The variable is the class name and must begin with an uppercase
letter.
The colon variable defines the superclass for the class and, if not given,
will default to class Object.
Colon variables
The colon must immediately precede the variable.
Instance variables
,
\
"\
;,
.\ .\ ~ \ ~ }
Appendix 2 Syntax Charts
Instance variables must begin with a lowercase letter.
Protocol
215
The vertical bar separating methods must be placed in column 1.
Method
Method Pattern
A unary selector is simply an identifier beginning with a lowercase
letter, for example sign.
216
"'
\;-
\
\
~ \
Appendix 2 Syntax Charts
A binary selector is one or two adjacent nonalphabetic characters,
except parenthesis, square braces, semicolon, or period, for example +.
A keyword selector is an identifier beginning with a lowercase letter
and followed by a colon, for example after:.
Argument variables must begin with a lowercase letter and must be
distinct from instance variables.
Temporary Variables
Temporary variables must begin with lowercase letters and must be
distinct from both instance and argument variables.
Statements
An expression preceded by an up arrow cannot be followed by a period
and another expression.
Expression
\ '\
\
\
. : - ~ ~ -;;.-
Appendix 2 Syntax Charts 217
The assignment arrow is a two-character sequence formed by a less
than sign «) followed by a minus sign (-).
Cascaded Expression
Simple Expression
Binary
Unary
\
"\
\ \
..~ ~ ;;.-
218
Appendix 2 Syntax Charts
Primary
A variable that begins with an uppercase letter is a class name; oth-
erwise, the variable must be instance, argument or temporary variable or
a pseudo variable name.
Continuation
\
.\.
Appendix 2 Syntax Charts
Block
219
The last statement in a block cannot be followed by a period.
Block Arguments
Literal
220
\
:....
Appendix 2 Syntax Charts
Number
Base
The integer value must be in the range 2 through 36.
Sign
Unsigned Number
Unsigned Fradion
\.
.'".
Appendix 2 Syntax Charts 221
Unsigned Integer
Uppercase letters are used to represent the digits 11-36 in bases greater
than 10.
Symbol
The character sequence following the sharp sign includes all nonspace
characters except period, parenthesis, or square braces.
"\
\
\ \
; ,'.0: ~ \
~ -;;.-
222 Appendix 2 Syntax Charts
String
To include a quote mark in a string, use two adjacent quote marks.
Character Constant
Bytearray
The unsigned integer must be in the range 0 through 255.
Array Constant
\ ~
\ ~ .
\. \
~ . : : . , .> ~ \
Array
Appendix 2 Syntax Charts 223
The lea,ding sharp sign can be omitted in symbols and arrays inside of an
array list. Binary selectors, keywords, and other sequences of chara,cters
are treated as symbols inside of an array.
Primitive
224 Appendix 2 Syntax Charts
Primitive Header
The variable must correspond to one of the primitive names. (See
Appendix 4.)
The keyword primitive or the primitive name must immediately follow
the angle bracket.
The unsigned integer must be a number in the range 0-255.
"\
~ ~ . -
Appendix 3
Class Descriptions
The messages accepted by the classes included in the Little Smalltalk
standard library are described in the following pages. A list of the classes
defined, where indentation is used to imply subclassing, is given below:
Object
UndefinedObject
Symbol
Boolean
True
False
Magnitude
Char
Number
Integer
Float
Radian
Point
Random
Collection
Bag
Set
KeyedCollection
Dictionary
Smalltalk
SequenceableCollection
Interval
List
Semaphore
File
ArrayedCollection
Array
ByteArray
String
Block
Class
Process
225
226 Appendix 3 Class Descriptions
In the descriptions of each message the following notes may occur:
d Indicates the effect of the message differs slightly from that given in
(Goldberg 83).
n Indicates the message is not included as part of the language defined
in (Goldberg 83).
r Indicates that the protocol for the message overrides a protocol given
in some superclass. The message given a second time only where the
logical effect of this overriding is important. Some messages, such as
copy, are overridden in many classes but are not described in the
documentation because the logical effect remains the same.
Object
The class Object is a superclass of all classes in the system and is used to
provide a consistent basic functionality and default behavior. Many meth-
ods in class Object are overridden in subclasses.
Responds to
asString
asSymbol
class
copy
deepCopy
d do:
error:
,Return true if receiver and argument are the same
object; false if not.
Inverse of = =. Return true if receiver and argument
are different ofjects; false if not.
Return a string representation of the re.ceiver; by de-
fault this is the same as printString, although one or
the other is redefined in many subclasses.
Return a symbol representing the receiver.
Return object representing the class of the receiver.
Return shallowcopy of receiver. Many subclasses re-
define shallowCopy.
Return the receiver. This method is redefined in many
subclasses.
The argument must be a one-argument block. Exe-
cute the block on every element of the receiver c o l ~
lection. Elements in the receiver collection are
enumerated using first and next (below), so the defaul t
behavior is merely to execute the block using the re-
ceiver as argument.
Argument must be a String. Print argument string as
error message. Return nil.
\
\
\
\
..~ ~ }- ).. . . ~
Appendix 3 Class Descriptions 227
n first
isKindOf:.
isMemberOf:
isNil
n next
notNii
print
printString
respondsTo:
shallowCopy
Examples
Return first item in sequence, which is, by default,
simply the receiver. See next, below.
Argument must be a Class. Return true if class of
receiver, or any superclass thereof, is the same as ar-
gument.
Argument must be a Class. Return true if receiver is
instance of argument class.
Test whether receiver is object nil.
Return next item in sequence, which is, by default,
nil. This message is redefined in classes which rep-
resent sequences, such as Array or Dictionary.
Test if receiver is not object nil.
Display print image of receiver on the standard out-
put.
Return a string representation of receiver. Objects
which do not redefine printString and which there-
fore do not have a printable representation, return
their class name as a string.
Argument must be a symbol. Return true if receiver
will respond to the indicated message.
Return the receiver. This method is redefined in many
subclasses.
Printed result
7 ~ ~ 7 . 0
7 asSymbol;
7 class
7 copy
7 isKindOf: Number
7 isMemberOf: Number
7 isNii
7 respondsTo: # +
True
#7
Integer
7
True
False
False
True
Object
UndefinedOhject
The pseudo variable nil is an instance (usually the only instance) of the
class UndefinedObject. The variable nil is used to represent undefined
values and is also typically returned in error situations. The variable nil
is also used as a terminator in sequences, as, for example, in response to
the message next when there are no further elements in a sequence.
228
\.
,'.0:,
Responds to
, isNil
, notNil
, print-
String
Examples
Appendix 3 Class Descriptions
Overrides method found in Object. Return true.
Overrides method found in Object. Return false.
Return II nil".
Printed result
nil isNil True
Object
Symbol
Instances of the class Symbol are created either by their literal represen-
tation, which is a pound sign followed by a string of nonspace characters
(for example #aSymbol), or by the message asSymbol being passed to an
object. Symbols cannot be created using new. Symbols are guaranteed to
have unique representations; that is, two symbols representing the same
characters will always test equal to each other. Inside of literal arrays, the
leading pound signs on symbols can be eliminated, for example: #(these
are symbols).
Responds to
,
,
,
asString
printString
Return true if the two symbols represent the same char-
acters; false otherwise.
Return a string representation of the symbol without the
leading pound sign.
Return a string representation of the symbol, including
the leading pound sign.
Examples
Pri nted resuIt
#abc = = # abc
. #abc = = # ABC
#abc ~ ~ # ABC
#abc printString
label asSymbol
True
False
True
# abc
# abc
\.
,'.-.
Appendix 3 Class Descriptions
'\
,\.
229
Object
Boolean
The class Boolean provides protocol for manipulating true and false val-
ues. The pseudo variables true and false are instances of the subclasses
of Boolean: True and False, respectively. The subclasses True and False,
in combination with blocks, are used to implement conditional control
structures. Note, however, that the bytecodes may optimize conditional
tests by generating code inline, rather than using message passing. Note
also that bit-wise boolean operations are provided by class Integer.
Responds To
& The argument must be a boolean. Return the logical conjunction
(and) of the two values.
The argument must be a boolean. Return the logical disjunction
(or) of the two values.
and: The argument must be a block. Return the logical conjunction
(and) of the two values. If the receiver is false, the second argument
is not used; otherwise, the result is the value yielded in evaluating
the argument block.
or: The argument must be a block. Return the logical disjunction (or)
of the two values. If the receiver is true, the second argument is
not used; otherwise, the result is the value yielded in evaluating
the argument block.
eqv: The argument must be a boolean. Return the logical equivalence
(eqv) of the two values.
xor: The argument must be a boolean. Return the logical exclusive or
(xor) of the two values.
Examples
Printed result
(1 > 3) & (2 < 4) False
(1 > 3) I (2 < 4) True
(1 > 3) and: [2 < 4] False
Object
Boolean
True
The pseudo variable true is an instance (usually the only instance) of the
class True. In conjunction with blocks, the class True is used to implement
conditional transfer of control.
230
\
), .
Appendix 3 Class Descriptions
Responds To
ifTrue:
ifFalse;
ifTrue: ifFalse:
ifFalse:ifTrue:
not
Examples
Return the result of evaluating the argument block.
Return nil.
Return the result of evaluating the first argument block.
Return the result of evaluating the second argument
block.
Return false.
Printed result
(3 < 5) not False
(3 < 5) ifTrue: [17] 17
Object
Boolean
False
The pseudo variable false is an instance (usually the only instance) of the
class False. In conjunction with blocks, the class False is used to imple-
ment conditional transfer of control.
ifTrue:
ifFalse:
ifTrue:ifFaIse;
ifFaIse: iftrue:
not
Examples
Return nil.
Return the result of evaluating the argument block.
Return the result of evaluating the second argument
block.
Return the result of evaluating the first argument block.
Return true.
Printed result
(1 < e) ifTrue: [17] 17
(1 < 3) ifFalse; [17] nil
Object
Magnitude
The class Magnitude provides protocol for those subclasses possessing a
linear ordering. For the sake of efficiency, most subclasses redefine some
or all of the relational messages. All methods are defined in terms of the
basic messages <, = and >, which are in turn defined circularly in terms
\
\
\ .
..
.'
.\
.'
Appendix 3 Class Descriptions 231

min:
max:
>=
>
between: and;
of each other. Thus each subclass of Magnitude must redefine at least one
of these messages.
< Relational less than test. Returns a boolean.
< = . Relational less than or equal test.
= Relational equal test. Note that this differs from
which is an object equality test.
Relational not equal test, opposite of =.
than or equal test.
Relational greater than test.
Relational test for inclusion.
Return the maximum of the receiver and argument

Return the minimum of the receiver and argument
value.

Printed result
$A max: $a $a
4 between: 3.1 and: (17/3) True
Examples
Pri nted resuIt
3 < 4.1
3 + 4.1
3.14159 exp
ogamma
5 reci procaI
0.5 radians
13 roundTo: 5
12 trullcateTo: 5
True
7.1
23.1406
40320
0.2
0.5 radians
15
10
.',
Object
Magnitude
.Char
The class Char defines protocol for objects with character values. Char-
acters possess an ordering given by the underlying representation; how-
ever, arithmetic is not defined for character values. Characters are written
literally by preceding the character desired with a dollar sign, for example:
$a $B $$.
232
,
'.
\
\
-, -,
.,
: . ~
Appendix 3 Class Descriptions
Responds to
r
--
asciiValue
asLowercase
asUppercase
r asString
digitValue
r
isAI ph.aNumeric
isDigit
isLetter
isLowercase
isSeparator
isUppercase
isVowel
printString
Object equality test. Two instances of the same
character always test equal.
Return an Integer representing the ASCII value of
the receiver.
If the receiver is an uppercase letter, returns the
same letter in lowercase; otherwise, returns the re-
ceiver.
If the receiver is a-lowercase letter, returns the same
letter in uppercase; otherwise, returns the receiver.
Return a length one string containing the receiver.
Does not contain leading dollar sign; compare to
printString.
If the receiver represents a number (for example
$9), return the digit value of the number. If the re-
ceiver is an uppercase letter (for example $B), re-
turn the position of the number in the uppercase
letters + 10, ($B returns 11, for example). If the
receiver is neither a digit nor an uppercase letter,
an error is given and nil returned.
Respond true if receiver is either digit or letter;
false otherwise.
Respond true if receiver is a digit; false otherwise.
Respond true if receiver is a letter, false otherwise.
Respond true if receiver is a lowercase letter; false
otherwise.
Respond true if receiver is a space, tab or newline;
false otherwise.
Respond true if receiver is an uppercase letter; false
otherwise.
Respond true if receiver is $a, $e, $i, $0, or $u, in
either upper- or lowercase.
Respond with a string representation of the char--
acter value. Includes leading dollar sign; compare
to asString, which does not include $.
Examples
Printed result
$A < $0 False
$A asciiValue 65
Appendix 3 Class Descriptions
$A asStri ng A
$A printString $A
$A isVowel True
$A digitValue 10
$ asciiValue radix: 8 8r40
Object
Magnitude
Number
233
The class Number is an abstract superclass for Integer and Float. In-
stances of Number cannot be created directly. Relational messages and
many arithmetic messages are redefined in each subclass for arguments
of the appropriate type. In general, an error message is given and nil
returned for illegal arguments.
Responds To
+
*
/
n
t
@
abs
exp
n gamma
In
log:
negated
negative
n pi
positive
n raqians
raisedTo:
reciprocal
roundTo:
Mixed type addition.
Mixed type subtraction.
Mixed type multiplication.
Mixed type division.
Exponentiation, same as raisedTo:
Construct a point with coordinates being the receiver
and the argument.
Absolute value of the receiver.
e raised to the power.
_Return the gamma function (generalized factorial)
evaluated at the receiver.
Natural logarithm of the receiver.
Logarithm in the given base.
The arithmetic inverse of the receiver.
True if the receiver is negati ve.
Return the approximate value of the receiver multi-
plied by (3.1415926...).
True if the recei ver is posi tive.
Argument converted into radians.
The receiver raised to the argument value.
The ari thmetic reciprocal of the receiver.
The receiver rounded to units of the argument.
"
"\
\ \
.~ ~ ~
234
sign
sqrt .
squared
strictlyPositive
to:
to:by:
truncatedTo:
Appendix 3 Class Descriptions
Return - 1, OJ or 1 depending upon whether the re-
ceiver is negative, zero, or positive.
Square root, nil if the receiver is less than zero.
Return the receiver multiplied by itself.
True if the receiver is greater than zero.
Interval from receiver to argument value with step
of 1.
Interval from receiver to argument in given steps.
The receiver truncated to units of the argument.
Object
Magnitude
Number
Integer .
The class Integer provides protocol for objects with integer values.
Responds To
r = =
II
allMask:
ariyMask:
asCharacter
asFloat
bitAnd:
bitAt:
bitlnvert
Object equality test. Two integers representing the
same value are considered to be the same object.
Integer quotient, truncated towards negative infinity
(compare to quo:).
Integer remainder, truncated towards negative infinity
(compare to rem:).
Argument must be Integer. Treating receiver and argu-
ment as bit strings, return true if all bits with value in
argument correspond to bits with 1 value in the re-
ceiver.
Argument must be Integer. Treating receiver and argu-
ment as bit strings, return true if any bit with 1 value
in argument corresponds to a bit with 1 value in the
receiver.
Return the Char with the same underlying ASCII rep-
resentation as the low order eight bits of the receiver.
Floating point value with same magnitude as receiver.
Argument must be Integer. Treating the receiver and
argument as bit strings, return logical and of values.
Argument must be Integer greater than 0 and less than
underlying word size. Treating receiver as a bit string,
return the bit value at the given position, numbering
from low order (or rightmost) position.
Return the receiver with all bit positions inverted.
\ \
\
~ ~ ~ \ .. ~
Appendix 3 Class Descriptions 235
bitOr:
bitShift:
bitXor:
even
factorial
ged:
highBit
lem:
noMask:
odd
quo:
radix:
rem:
timesRepeat:
Examples
5 + 4
5 allMask: 4
4 allMask: 5
5 anyMask: 4
5 bitAnd: 3
5 bitOr: 3
5 bitlnvert
254 radix: 16
- 5/ /4
-5 quo: 4
-5"'- "'-4
-5 rem: 4
8 factorial
Return logical or of values.
Treating the receiver as a bit string, shift bit values
by amount indicated in argument. Negative values
shift right; positive values shift left.
Return logical exclusive-or of values.
Return true if receiver is even; false otherwise.
Return the factorial of the receiver. Return as Float
for large numbers.
Argument must be Integer. Return the greatest com-
mon divisor of the receiver and argument.
Return the location of the highest 1 bit in the receiver.
Return nil- for receiver zero.
Argument must be Integer. Return least common mul-
tiple of receiver and argument.
Argument must be Integer. Treating receiver and ar-
gument as bit strings, return true if no 1 bit in the
argument corresponds to a 1 bit in the receiver.
Return true if receiver is odd; false otherwise.
Return quotient of receiver divided by argument.
Return a string representation of the receiver value
printed in the base represented by the argument. Ar-
gument value must be less than 36.
Remainder after receiver is divided by argument
value.
Repeat argument block the number of times given by
the receiver.
Printed result
7
True
False
True
1
7
-6
16rFE .
-2
-1
1
-1
40320
236
"\
:'>..
Appendix 3 Class Descriptions
Object
Magnitude
Number
Float
The class Float provides protocol for objects with floating point values.
Responds -To
r
= =
n
i
arcCos
arcSin
arcTan
asFloat
ceiling
coerce:
exp
floor
fractionPart
n gamma
integerPart
In
radix:
rounded
sqrt
truncated
Object equality test. Return true if the receiver and
argument represent the same floating point value.
Floating exponentiation.
Return a Radian representing the arcCos of the re-
ceiver.
Return a Radian representing the arcSin of the re-
ceiver.
Return a Radian representing the arcTan of the re-
ceiver.
Return the receiver.
Return the integer ceiling of the receiver.
Coerce the argument into being type Float.
Return e raised to the receiver value.
Return the integer floor of the receiver.
Return the fractional part of the receiver.
Return the value of the gamma function applied to the
receiver value.
Return the integer part of the receiver.
Return the natural log of the receiver.
Return a string containing the printable representation
of the receiver in the given radix. Argument must be
an Integer less than 36.
Return the receiver rounded to the nearest integer.
Return the square root of the receiver.
Return the receiver truncated to the nearest integer.
Examples
Printed result
4.2 * 3
2.1 i 4
2.1 raisedTo: 4
12.6
19.4481
19.4481
\
, ~ .
Appendix 3 Class Descriptions 237
0.5 arcSin
2.1 reciprocal
4.3 sqrt
0.523599 radians
0.47619
2.07364
Object
Magnitude
Radian
The class Radian is used to represent radians. Radians are a unit of mea-
surement, independent of other numbers. Only radians will respond to the
trigonometric functions such as sin and cos. Numbers can be converted
into radians by passing them the message radians. Similarly, radians can
be converted into numbers by sending them the message asFloat. Notice
that only a limited range of arithmetic operations are permitted on Radi-
ans. Radians are normalized to be between 0 and 2'R' by adding or s u b ~
tracting multiples of 2'R'.
*
I
sin
cos
Responds to
+ Argument must be a radian. Add the two radians together and
return the normalized result.
Argument must be a Radian. Subtract the argument from the
receiver and return the normalized result.
Argument must be a number. Multiply the receiver by the ar-
gument amount and return the normalized result.
Argument must be a number. Divide the receiver by the argu-
ment amount and return the normalized result.
asFloat Return the receiver as a floating point number.
Return a floating point number representing the cosine of the
receiver.
Return a floating point number representing the sine of the re-
ceiver.
Return a floating point number representing the tangent of the
receiver.
tan
Examples
Printed result
0.5236 radians sin
0.5236 radians cos
0.5236 radians tan
0.5 arcSin asFloat
0.5
0.866025
0.577352
0.532599
238 Appendix 3 Class Descriptions
Object
Magnitude
Point
Points are used to represent pairs of quantities, such as coordinate pairs.
Responds To
<
<=
>=
*
/
/ /
+
abs
dist:
max:
min:
transpose
x
x:
x:y:
y
y:
Examples
True if both values of the receiver are less than the corre-
sponding values in the argument.
True if the first value is less than or equal to the correspond-
ing value in the argument, and the second value is less than
the corresponding value in the argument.
True if both values of the receiver are greater than or equal
to the corresponding values in the argument.
Return a new point with coordinates multiplied by the ar-
gument value.
Return a new point with coordinates divided by the argument
value.
Return a new point with coordinates divided by the argument
value.
Return a new point with coordinates offset by the corre-
sponding values in the argument.
Return a new point with coordinates having the absolute
value of the receiver.
Return the Euclidean distance between the receiver and the
argument point.
The argument must be a Point. Return the lower right corner
of the rectangle defined by the receiver and the argument.
The argument must be a Point. return the upper left corner
of the rectangle defined by the receiver and the argument.
Return a new point with coordinates being t h ~ transpose of
the receiver.
Return the first coordinate of the receiver.
Set the first coordinate of the receiver.
Sets both coordinates of the receiver.
Return the second coordinate of the receiver.
Set the second coordinate of the receiver.
Printed result
(10@12) < (11 @14) True
(10@12) < (11 @11) False
\
,'.0:.
Appendix 3 Class Descriptions
\
~ . - } .
239
(10@12) max: (11@11)
(10@12) min: (11@11)
(10@12) dist: (11@14)
(10@12) transpose
11@12
10@11
2.23607
12@10
Object
Random
The class Random provides protocol for random number generation.
Sending the message next to an instance of Random results in a Float
between 0.0 and 1.0 randomly distributed. By default, the pseudo random
sequence is the same for each object in class Random. This can be altered
by using the message randomize.
Responds to
n between:and:
n first
next
d next:
n randlnteger:
n randomize
Examples
Return a randol11 number uniformly distributed be-
tween the two arguments.
Return a random riumber between 0.0 and 1.0. This
message merely provides consistency with protocol
for other sequences such as Arrays or Intervals.
Return a random number between 0.0 and 1.0.
Return an Array containing the next n random num-
bers where n is the argument value.
The argument must be an integer. Return a random
integer between 1 and the value given.
Change the pseudo-random number generator seed
by a time-dependent value.
Pri nted resuIt
~ Random new
next
next
next: 3
randInteger: 12
between: 4 and 17.5
0.759
0.157
#( 0.408 0.278 0.547 )
5
10.0
Object
Collection
The class Collection provides protocol for groups of objects such as Arrays
or Sets. The different forms of collections are distinguished by several
characteristics, among them whether the size of the collection is fixed or
240 Appendix 3 Class Descriptions
unbounded, the presence of absence of an ordering, and their insertion or
access method. For example, an Array is a collection with a fixed size and
ordering, indexed by integer keys. A Dictionary, on the other hand, has
no fixed size or ordering and can be indexed by arbitrary elements. Never-
theless, Arrays and pictionarys share many features in common such as
their access method (at: and at.·put:) and the ability to respond to collect:,
select:, and many other messages.
The table below lists some of the characteristics of several forms of
collections:
Creation Size Insertion Access Removal
Name Method fixed? Ordered? Method Method Method
Bag/Set new no no add: includes: remove:
Dictionary new no no at:put: at: removeKey:
Interval n to: m yes yes none at: none
Ust new no yes addFirst: first remove:
addLast: last
Array new: yes yes at:put: at: none
String new: yes yes at:put: at: none
The list below shows messages that are shared in common by all col-
lections.
Responds to
addAII:
asArray
asBag
The argument must be a collection. Add all the
elements of the argument collection to the re-
ceiver collection.
Return a new collection of type Array containing
the elements from the receiver collection. If the
receive was ordered, the elements will be in the
same order in the new collection; otherwise, the
elements will be in an arbitrary order.
Return a new collection of type Bag containing
the elements from the receiver collection.
Appendix 3 Class Descriptions
\.
241
n asList Return a new collection of type List containing
the elements from the receiver collection. If the
receiver was ordered, the elements will be in the
same order in the new collection, otherwise the
elements will be in an arbitrary order.
asSet Return a new collection of type Set containing the
elements from the receiver collection.
asString Return a new collection of type String containing
the elements from the receiver collection. The ele-
ments to be included must all be of type Charac-
ter. If the receiver was ordered, the elements will
be in the same order in the new collection; oth-
erwise, the elements will be listed in an arbitrary
order.
coerce: The argument must be a collection. Return a col-
lection of the same type as the receiver containing
elements from the argument collection. This mes-
sage is redefined in most subclasses of collection.
collect: The argument must be a one-argument block.
Return a new collection like the receiver contain-
ing the result of evaluating the argument block on
each element of the receiver collection.
detect: The argument ~ u s t be a one-argument block.
Return the first element in the receiver collection
for which the argument block evaluates true.
Report an error and return nil if no such element
exists. Note that in unordered collections (such as
Bags or Dictionarys the first element to be en-
countered that will satisfy the condition may not
be easily predictctble.
detect: ifAbsent: Return the first element in the receiver collection
for which the first argument block evaluates true.
Return the result of evaluating the second argu-
ment if no such element exists.
do: The argument must be a one-argument block.
Evaluate the argument block on each element in
the receiver collection.
includes: Return true if the receiver collection contains the
argument.
inject:into: The first argument must be a value, the second a
two-argument block. The second argument is
evaluated once for each element in the receiver
collection, passing as arguments the result of the
242 Appendix 3 Class Descriptions
previous evaluation (starting with the first argu-
ment) and the element. The value returned is the
final value generated.
isEmpty Return true if the receiver collection contains no
elements.
occurrencesOf: Return the number of times the argument occurs
in the receiver collection.
remove: Remove the argument from the receiver collec-
tion. Report an error if the element is not con-
tained in the receiver collection.
remove: ifAbsent: Remove the first argument from the receiver col-
lection. Evaluate the second argument if not pres-
ent.
reject: The argument must be a one-argument block.
Return a new collection like the receiver contain-
ing all elements for which the argument block re-
turns false.
select: The argument must be a one-argument block.
Return a new collection like the receiver contain-
ing all elements for which the argument block re-
turns true.
size Return the number of elements in the receiver col-
lection.
Examples
Printed result
~ labacadabra
l
size
asArray
asBag
asSet
occurencesOf: $a
reject: [:x I x isVowel]
10
#( $a $b $a $c $a $d $a $b $r$a )
Bag ( $a $a $a $a $a $r $b $b $c $d)
Set ( $a $r $b $c $d )
5
bcdbr
Object
Collection
Bag/Set
Bags and Sets are each unordered collections of elements. Elements in
the collections do not have keys but are added and removed directly. The
difference between a Bag and a Set is that in a Bag each element can
occur any number of times; whereas only one copy is inserted into a Set.
\
\.
\::
'\
~ ~ . - ~
. ~ ~
Appendix 3 Class Descriptions 243
Responds to
add:
add: withOccurences:
n first
n next
Examples
Add the indicated element to the receiver col-
lection.
(Bag only) Add the indicated element to the
receiver Bag the given number of times.
Return the first element from the receiver col-
lection. Because the collection is unordered,
the first element depends upon certain values
in the internal representation and is not guar-
anteed to be any specific element in the col-
lection.
Return the next element in the collection. In
conjunction with first, this can be used to ac-
cess each element of the collection in turn.
Printed result
i ~ (1 to: 6) asBag Bag ( 1 2 3 4 5 6 )
i size 6
i select: [:x I (x " " 2) strictly Positive] Bag ( 1 3 5 )
i collect: [:x I x " " 3] Bag ( 0 0 1 1 2 2 )
j ~ ( i collect: [:x I x " "3] ) asSet Set ( 0 1 2 )
j size 3
Note: Since Bags and Sets are unordered, there is no way to establish a
mapping between the elements of the Bag i in the example above and the
corresponding elements in the collection that resulted from the message
collect: [:x I x " " 3].
Object
Collection
KeyedCollection
The class KeyedCollection provides protocol for collections with keys,
such as Dictionarys and Arrays. Since each entry in the collection has
both a key and value, the method add: is no longer appropriate. Instead,
the method at:put:, which provides both a key and a value, must be used.
Responds to
asDictionary Return a new collection of type Dictionary con-
taining the elements from the receiver collection.
244 Appendix 3 Class Descriptions
at: Return the item in the receiver collection whose
key matches the argument. Produce an error mes-
sage and return nil if no item is currently in the
receiver collection under the given key.
at: ifAbsent; Return the element stored in the dictionary under
the key given by the first argument. Return the
result of evaluating the second argument if no
such element exists.
atAII: put: The first argument must be a collection contain-
ing keys valid for the receiver. Place the second
argument at each location given by a key in the
first argument.
binaryDo: The argument must be a two-argument block.
This message is similar to do:, however both the
key and the element value are passed as argument
to the block.
inciudesKey: Return true if the indicated key is valid for the
receiver collection.
indexOf: Return the key value of the first element in the
receiver collection matching the argument. Pro-
duces an error message if no such element exists.
Note that, as with the message detect:, in unor-
dered collections the first element may not be re-
lated in any way to the order in which elements
were placed into the collection but is rather im-
plementation dependent.
indexOf: ifAbsent Return the key value of the first element in the
receiver collection matching the argument.
Return the result of evaluating the second argu-
ment if no such element exists.
keys Return a set containing the keys for the receiver
collection.
keysDo; The argument must be a one-argument block.
Similar to do: except that the values passed to the
block are the keys of the receiver collection.
keysSelect; Similar to select except that the selection is made
on the basis of keys instead of values.
removeKey: Remove the object with the given key from the
receiver collection. Print an error message and re-
turn nil if no such object exists. Return the value
of the deleted item.
removeKey:ifAbsent: Remove the object with the given key from the
receiver collection. Return the result of evaluating
the second argument if no such object exists.
\.
,'.-.
Appendix 3 Class Descriptions 245
\
,\.
values
Examples
Return a Bag containing the values from the re-
ceiver collection.
Printed result
i ~ 'abacadabra'
i atAII: (1 to: 7 by: 2) put: $e
i indexOf: $r
i atAlI: i keys put: $z
i keys
i values
#(how odd) asDictionary
ebecedebra
9
zzzzzzzzzz
Set ( 1 2 3 4 5 6 7 8 9 10)
Bag ($z $z $z $z $z $z $z $z $z $z )
Dictionary ( 1 @ #how 2 @ odd)
Object
Collection
KeyedCollection
Dictionary
ADictionary is an unordered collection of elements as are Bags and Sets.
However, unlike these collections, when elements are inserted and re-
moved from a Dictionary, they must reference an explicit key. Both the
key and value portions of an element can be any object although commonly
the keys are instances of Symbol or Number.
Responds to
at:put: Place the second argument· into the receiver collection
under the key given by the first argument.
currentKey Return the key of the last element yielded in response to
a first or next request.
n first Return the first element of the receiver collection.
Return nil if the receiver collection is empty.
n next Return the next element of the receiver collection, or nil
if no such element exists.
Examples
Pri nted resu It
i ~ Dictionary new
i at: #abc put: # def
i at: # pqr put: # tus
i at: # xyz put: # wrt
246
print
size
at: # pqr
indexOf: # tus
keys
values
collect: [:x I x asString at: 2]
Appendix 3 Class Descriptions
Dictionary ( # abc @ # def # pqr @ # tus # xyz @ # wrt)
3
# tus
#pqr
Set ( # abc # pqr # xyz )
Bag ( # wrt # def # tus)
Dictionary ( # abc @ $e # pqr @ $u # xyz @ $r)
Object
Collection
KeyedCollection
Dictionary
Smalltalk
The class Smalltalk provides protocol for the pseudo-variable smalltalk.
Since it is a subclass of Dictionary, this variable can be used to store
information and thus provide a means of communication between objects.
Other messages modify various parameters used by the Little Smalltalk
system. Note that the pseudo-variable smalltalk is unique to the Little
Smalltalk system and is not part of the Smalltalk-80 programming envi-
ronment.
Responds To
n date
n display
n displayAssign
n doPrimitive: withArguments:
n getString
n noDisplay
Return the current date and time as a
string.
Set execution display to display the re-
sult of every expression typed except
assignments. Note that the display be-
havior can also be modified using the
-d argument on the command line.
Set execution display to display the re-
sult of every expression typed includ-
ing assignment statements.
Execute the indicated primitive with
arguments given by the second array.
Afew primitives (such as those dealing
with process management) cannot be
executed in this manner.
Return text typed at the terminal as a
String. All characters up to the next
newline are accepted.
Turn off execution display (no results
will be displayed unless explicitly re-
quested by the user).
Appendix 3 Class Descriptions 247
d perform:withArguments:
n sh:
n time:
Examples
Send indicated message to the receiver
using the arguments given. The first
value in the argument array is taken
to be the receiver of the message.
Results are unpredictable if the num-
ber of arguments is not appropriate for
the given message.
The argument, which must be a string,
is executed as a Unix command by the
shell. The value returned is the ter-
mination status of the shell.
The argument must be a block. The
block is executed and the number of
seconds elapsed during execution re-
turned. Time is accurate to within
only about one second.
Pri nted resuIt
smalltalk date
smalltalk perform: # + withArguments: #(2 5)
smalltalk doPrimitive: 10 withArguments: #(2 5)
Fri Apr 12 16:15:42 1985
7
7
Object
Collection
KeyedCollection
SequenceableCollection
The class SequenceableCollection contains protocol for collections that
have a definite sequential ordering and are indexed by integer keys. Since
there is a fixed order for elements, it is possible to refer to the last element
in a SequenceableCollection.
Responds to
copyFrom:to:
copyWith:
Append the argument collection to the receiver
collection, returning a new collection of the same
type as the receiver.
Return a new collection like the receiver con-
taining the designated subportion of the receiver
collection.
Return a new collection like the receiver with the
argument added to the end.
\
,
\
"\
~ '.0: ~ \
.,.
248
copyWithout:
equals: starti ngAt:
findFirst:
findFirst: ifAbsent:
findLast:
fi ndLast: ifAbsent:
firstKey
indexOfSubCollection:
startingAt:
indexOfSubcollection:
startingAt: ifAbsent:
last
lastKey
replaceFrom:to: with:
repl aceFrom:to: with:
startingAt:
Appendix 3 Class Descriptions
Return a new collection like the receiver with all
occurrences of the argument removed.
The first argument must be a Sequenceable-
Collection. Return true if each element of the re-
ceiver collection is equal to the corresponding
element in the argument offset by the amount
given in the second argument.
Find the key for the first element whose value
satisfies the argument block. Produce an error
message if no such element exists.
Both arguments must be blocks. Find the key for
the first element whose value satisfies the first
argument block. If no such element exists, return
the value of the second argument.
Find the key for the last element whose value sat-
isfies the argument block. Produce an error mes-
sage if no such element exists.
Both arguments must be blocks. Find the key for
the last element whose value satisfies the first
argument block. If no such element exists, return
the value of the second argument.
Return the first key valid for the receiver collec-
tion.
Starting at the position given by the second ar-
gument, find the next block of elements in the
receiver collection which match the collection
given by the first argument and return the index
for the start of that block. Produce an error mes-
sage if no such position exists.
Similar to indexOfSubCollection:startingAt:, ex-
cept that the result of the exception block is pro-
duced if no position exists matching the pattern.
Return the last element in the receiver collection.
Return the last key valid for the receiver collec-
tion.
Replace the elements in the receiver collection in
the positions indicated by the first two argu-
ments with values taken from the collection
given by the third argument.
Replace the elements in the receiver collection in
the positions indicated by the first two argu-
ments with values taken from the collection
\
\.
i
~ ~
.'
,<
Appendix 3 Class Descriptions 249
n reversed
reverseDo:
n sort
n sort:
with:do:
Examples
given in the third argument, starting at the po-
sition given by the fourth argument.
Return a collection like the receiver with ele-
ments reversed.
Similar to do: except that the items are pre-
sented in reverse order.
Return a collection like the receiver with theele-
ments sorted using the comparison < =. Ele-
ments must be able to respond to the binary
message < =.
The argument must be a two-argument block
which yields a boolean. Return a collection like
the receiver, sorted using the argument to com-
pare elements for the purpose of ordering.
The second argument must be a two-argument
block. Present one element from the receiver col-
lection and from the collection given by the first
argument in turn to the second argument block.
An error message is given if the collections do
not have the same number of elements.
Pri nted resuIt
~ 'abacadabra
'
copyFrom:4 to: 8
copyWith: $z
copyWithout: $a
findFirst: [:x I x > $m]
indexOfSubCollection: 'dab 'startingAt: 1
reversed
I i reversed
sort: [: x :y I x > = y]
cadab
abacadabraz
bcdbr
9
6
arbadacaba
abacadabraarbadacaba
rdcbbaaaaa
Object
Collection
KeyedCollection
SequenceableCollection
Interval
The class Interval represents a sequence of numbers in an arithmetic
sequence, either ascending or descending. Instances of Interval are created
250
).. ,
Appendix 3 Class Descriptions
by numbers in response to the message to: or ta:by:. In conjunction with
the message do:, Intervals create a control structure similar to do or for
loops in Angol-like languages. For example:
(1 to: 10) do: [:x Ix print]
will print the numbers 1 through 10. Although Intervals are a collection,
additional values cannot be added. Intervals can, however, be accessed
randomly by using the message at:.
Responds to
first
from:to:by:
next
size
Examples
Produce thefirst element from the interval. In conjunction
with last, this message may be used to produce each ele-
ment from the interval in turn. Note that Intervals also
respond to the message at:, which can be used to produce
elements in an arbitrary order.
Initialize the upper and lower bounds and the step size for
the receiver. (This is used principally internally by the
method for number to create new Intervals).
Produce the next element from the interval.
Return the number of elements that will be generated in
producing the interval.
Printed result
(7 to: 13 by: 3) asArray
(7 to: 13 by: 3) at: 2
(1 to: 10) inject: 0 into [:x :y Ix + y]
(7 to: 13) copyFrom: 2 to: 5
(3 to: 5) copyWith: 13
(3 to: 5) copyWithout: 4
(2 to: 4) equals: (1 to: 4) startingAt:2
# ( 7 10 13 )
10
55
# ( 8 9 10 11 )
#(34513)
# ( 3 5 )
True
Object
Collection
KeyedCollection
SequenceableCollection
List
Lists represent collections with a fixed order but indefinite size. No keys
are used, and elements are added or removed from one end or the other.
Used in this way, Lists can perform as stacks or as queues. .The table below
illustrates how stack and queue operations can be implemented in terms
of messages to instances of List.
\
\ \
\
~ . ~
,.
~ \ ~ \
stack operations
Appendix 3 Class Descriptions
queue operations
251
push addLast:
pop removeLast
top last
test empty isEmpty
add
first in queue
remove first in queue
test empty
addLast:
first
removeFirst
isEmpty
Responds to
add:
addAIIFirst:
addAIILast:
addFirst:
addLast:
removeFirst
removeLast
Examples
Add the element to the beginning of the receiver collection.
This is the same as addFirst:.
The argument must be a SequenceableCollection. The ele-
ments of the argument are added, in order, to the front of
the receiver collection.
The argument must be a SequenceableCollection. The ele-
ments of the argument are added, in order, to the end of
the receiver collection.
The argument is added to the front of the receiver collec-
tion.
The argument is added to the back of the receiver collec-
tion.
Remove the first element from the receiver collection, re-
turning the removed value.
Remove the last element from the receiver collection, re-
turning the removed value.
Printed result
i ~ List new
i addFirst: 2 I 3
i add: $A
i addAIlLast: (12 to: 14 by: 2)
i print
i first
i removeLast
i print
List ( 0.6666 )
List ( 0.6666 $A 12 14)
0.6666
14
List ( 0.6666 $A 12 )
Object
Collection
KeyedCollection
SequenceableCollection
List
Semaphore
Semaphores are used to synchronize concurrently running Processes.
252 Appendix 3 Class Descriptions
Responds To
new: If created using neMJ, a Semaphore starts out with zero excess
signals. Alternatively, a Semaphore can be created with an ar-
bitrary number of excess signals by giving it an argument to
new:.
critical: The argument must be a block. The block is executed as a crit-
ical section during which no other critical section using the
same semaphore can execute.
signal If there is a process blocked on the semaphore, it is scheduled
for execution; otherwise, the number of excess signals is incre-
mented by one.
wait If there are excess signals associated with the semaphore, the
number of signals is decremented by one; otherwise, the current
process is placed on the semaphore queue.
Object
Collection
KeyedCollection
SequenceableCollection
File
AFile is a type of collection where the elements are stored on an external
medium, typically a disk. For this reason, although most operations on
collections are defined for files, many can be quite slow in execution. A
file can be opened on one of three modes. In character mode every read
returns a single character from the file. In integer mode every read returns
a single word as an integer value. In string mode every read returns a single
line as a String. For writing, character and string modes will write the
string representation of the argument, while integer mode must write only
a single integer.
Responds To
at:
at:put:
characterMode
currentKey
integerMode
Return the object stored at the indicated position. Po-
sition is given as a character count from the start of the
file.
Place the object at the indicated position in the file. Po-
sition is given as a character count from the start of the
file.
Set the mode of the receiver file to character.
Return the current position in the file, as a character
count from the start of the file.
Set the mode of the receiver file to integer.
\
,'.0:.
Appendix 3 Class Descriptions 253
open:
open:for:
read
size
stringMode
write:
Open the indicated file for reading. The argument must
be a String.
The for: argument must be one of Ir', IWI or 'r +I (see
fopen(3) in the Unix programmer's manual). Open the
file in the indicated mode.
Return the next object from the file.
Return the size of the file, in character counts.
Set the mode of the receiver file to string.
Write the argument into the file.
Object
Collection
KeyedCollection
SequenceableCollection
ArrayedCollection
The class ArrayedCollection provides protocol for collections with a fixed
size and integer keys. Unlike other collections, which are created using the
message new, instances of ArrayedCollection must be created using the
one-argument message new:. The argument given with this message must
be a positive integer representing the size of the collection to be created.
In addition to the protocol shown, many of the methods inherited from
superclasses are redefined in this class.
Responds to
= The argument must also be an Array. Test whether the
receiver and the argument have equal elements listed
in the same order.
at: ifAbsent: Return the element stored with the given key. Return
the result of evaluating the second argument if the key
is not valid for the receiver collection.
n padTo: Return an array like the receiver that is at least as long
as the argument value. Returns the receiver if it is al-
ready longer than the argument.
. Examples
Printed result
Ismall' = Ismail I
Ismall' = ISMALL'
Ismall' asArray
True
False
# ( $s $m $a $1 $1)
\ \
"\
\
~ 0:;-
';
.,
254 Appendix 3 Class Descriptions
. 1-
1
-----------'
Ismail' asArray = 'small'
# (1 2 3) padTo: 5
# (1 2 3) padTo: 2
True
# (1 2 3 nil nil)
# (1 2 3)
Object
Collection
KeyedCollection
SequenceableCollection
ArrayedCollection
Array
Instances of the class Array are perhaps the most commonly used data
structure in Smalltalk programs. Arrays are represented textually by a
pound sign preceding the list of array elements.
Responds to
at: Return the item stored in the position given by the argument.
An error message is produced and nil returned if the argument
is not a valid key.
at:put: Store the second argument in the position given by the first
argument. An error message is produced and nil returned if the
argument is not a valid key.
Examples
Printed result
i ~ # ( 110 101 97)
i size
i ~ i copyWith: 116
i ~ i collect: [:x I x asCharacter]
i asString
3
# ( 110 101 97 116)
# ( #n #e #a #t )
neat
Object
Collection
KeyedCollection
SequenceableCollection
ArrayedCollection
ByteArray
A ByteArray is a special form of array in which the elements must be
numbers in the range 0 through 255. Instances of ByteArray are given a
very compact encoding and are used extensively internally in the Little
Smalltalk system. A ByteArray can be represented textually by a pound
\.
\
\
\
,=:;' ~ \ ...-:.: ~ \
Appendix 3 Class Descriptions 255
sign preceding the list of array elements surrounded by a pair of square
braces.
Responds to
at: Return the item stored in the position given by the argument.
An error message is produced and nil returned if the argument
is not a valid key.
at:put: Store the second argument in the position given by the first
argument. An error message is produced and nil returned if the
argument is not a valid key.
Examples
Printed result
i +- # [ 110 101 97]
i size
i +- i copyWith: 116
i +- i asArray collect: [:x I x asCharacter]
i asString
3
#[ 110 101 97 116 ]
# ( #n #e #a #t)
neat
Object
Collection
KeyedColiection
SequenceableColiection
ArrayedCollection
String
Instances of the class Stringare similar to Arrays except that the individual
elements must be Character. Strings are represented literally by placing
single quote marks around the characters making up the string. Strings
also differ from Arrays in that Strings possess an ordering given by the
underlying ASCII sequence.
Responds to
<
<=
Concatenates the argument to the receiver string,
producing a neW string. If the argument is not a
String, it is first converted using printString.
The argument must be a String. Test if the re-
ceiver is lexically less than the argument. For pur-
poses of comparison, case differences are ignored.
Test if the receiver is lexically less than or equal
to the argument.
>
=
>
n asFloat
n aslnteger
r asSymbol
at:
\ \.
':: \
~ \
.' .'
.,
256 Appendix 3 Class Descriptions
Test if the receiver is lexically greater than or
equal to the argument.
Test if the receiver is lexically greater than the
argument.
Return the string converted into a floating point
value.
Return the string converted into an integer.
Return a Symbol with characters given by the
receiver string.
Return the character stored at the position given
by the argument. Produce an error message and
return nil if the argument does not represent a
valid key.
at:put: Store the character given by second argument at
the location given by the first argument. Produce
an error message and return nil if either argu-
ment is invalid.
n copyFrom:length: Return a substring of the receiver. The substring
is taken from the indicated starting position in
the receiver and extends for the given length. Pro-
duce an error message and return nil if the given
positions are not legal.
r copyFrom:to: Return a substring of the receiver. The substring
is taken from the indicated positions. Produce an
error message and return nil if the given positions
are not legal.
n print A line consisting of the receiver is displayed on
the terminal.
n printAt: The argument must be a Point which describes a
location on the terminal screen. The string is
printed at the specified location.
n printNoReturn The receiver is printed on the terminal without a
newline/carriage return. A subsequent print-
NoReturn would follow immediately on the same
line as the printed text.
size Return the number of characters stored in the
string.
sameAs: Return true if the receiver and argument string
match with the exception of case differences. Note
that the boolean message = inherited from
ArrayedCollection can be used to see if two
strings are the same including case differences.
\
.\
Appendix 3 Class Descriptions
Examples
Printed result
257
lexample! at: 2
Ibead! at: 1 put: $r
Ismall
'
> 'BIG
'
'small' sameAs: 'SMALL!
Itary! sort
112.3
1
asFloat
'Rats live on no evil Starl reversed
$x
read
True
True
arty
12.3
ratS live on no evil staR
Object
Block
Although it is easy for a programmer to think of blocks as a syntactic
construct or a control structure they are actually objects and share attri-
butes of all other objects in the Smalltalk system, such as the ability to
respond to messages.
Responds to:
fork
forkWith:
newProcess
n newProcessWith:
value
value:
Start the block executing as a Process. The
value nil is immediately returned, and the
Process created from the block is scheduled
to run in parallel with the current process.
Similar to fork, except that the array is
passed as arguments to the receiver block
prior to scheduling for execution.
A new Process is created for the block but
is not scheduled for execution.
Similar to newProcess except that the array
is passed as arguments to the receiver
block prior to being made into a process.
Evaluates the receiver block. Produces an
error message and returns nil if the re-
ceiver block requires arguments. Return
the value yielded by the block.
Evaluates the receiver block. Produces an
error message and returns nil if the re-
ceiver block does not require a single ar-
gument. Return the value yielded by the
block.
258
Appendix 3 Class Descriptions
value: value:
value: value: value:
value: value: value: value:
value: vaIue: value:
value:value:
whileTrue:
whileTrue
whileFalse:
whileFalse
Examples
Two-argument block evaluation.
Three-argument block evaluation.
Four-argument block evaluation.
Five-argument block evaluation.
The receiver block is repeatedly evaluated.
While it evaluates to true, the argument
block is also evaluated. Return l1il when the
receiver block no longer evaluates to true.
The receiver block is repeatedly evaluated
until it returns a value that is not true.
The receiver block is repeatedly evaluated.
While it evaluates to false, the argument
block is also evaluated. Return nil when the
receiver block no longer evaluates to false.
The receiver block is repeatedly evaluated
until it returns a value that is 'not false.
Printed result
['block indeed
l
] value
[:x :y I x + y + 3] value: 5 value: 7
block indeed
15
Object
Class
The class Class provides protocol for manipulating class instances. An
instance of class Class is generated for each class in the Smalltalk system.
New instances of this class are then formed by sending messages to the
class instance.
Responds to
n deepCopy:
n edit
The argument must be an instance of the receiver
class. A deepCopy of the argument is returned.
The user is placed into a editor, editing the file from
which the class description was originally obtained.
When the editor terminates, the class description will
be reparsed and will override the previous descrip-
tion. See also view (below).
). \
'.
\.
~ } .
): ~ \
Appendix 3 Class Descriptions 259
n list Lists all subclasses of the given class recursively. In
particular, Object list will list the names of all the
classes in the system.
new Anew instance of the receiver class is returned. If the
methods for the receiver contain protocol for neytJ, the
new instance will first be passed this message.
new: Anew instance of the receiver class is returned. If the
methods for the receiver contain protocol for new:J the
new instance will first be passed this message.
n respondsTo List all the messages to which the current class will
respond.
d respondsTo: The argument must be a Symbol. Return true if the
receiver class or any of its superclasses contains a
method for the indicated message. Return false oth-
erwise.
n shaIlowCopy: The argument must be an instance of the receiver
class. A shallowCopy of the argument is returned.
n superClass Return the superclass of the receiver class.
n variables Return an array containing the names of the instance
variables used in the receiver class.
n view Place the user into an editor viewing the class descrip-
tion from which the class was created. Changes made
to the file will not, however, affect the current class
representation.
Examples
Pri nted resuIt
Array new: 3
Bag respondsTo: #add:
SequenceableCol lection superCI ass
ArrayedCollection variables
# ( nil nil nil)
True
Keyed Collection
#( #current )
Object
Process
Processes are created by the system or by passing the message newProcess
or fork to a block; they cannot be created directly by the user. The current
process is always available as the value of the pseudo-variable selfProcess.
(Note that the pseudo-variable selfProcess. (Note that the pseudo-variable
selfProcess is unique to Little Smalltalk and is not part of the Smalltalk-
80-programming environment.)
260
\
\
\
\
,'i,
.,.
.'
Appendix 3 Class Descriptions
Responds To
block The receiver process is marked as being blocked. This is usu-
ally the result of a semaphore wait. Blocked processes are not
executed.
resume If the receiver process has been suspended, it is rescheduled
for execution.
suspend If the receiver process is scheduled for execution, it is marked
as suspended. Suspended processes are not executed.
state The current state of the receiver process is returned as a Sym-
bol.
terminate The receiver process is terminated. Unlike a blocked or sus-
pended process, a terminated process cannot be restarted.
unblock If the receiver process is currently blocked, it is scheduled for
execution.
yield Returns nil. As a side effect, however, if there are pending
processes, the current process is placed back on the process
queue and another process is started.
\
\,
\
" ; ~ ~ \
"
~ }-
Appendix 4
Primitives
The following chart gives the function performed by each primitive in the
Little Smalltalk system. The number to the left indentifies the primitive
and is used in the longer form of primitive call, such as
<primitive 10 i j >
The identifier in bold following the number is the name of the primitive
and is used in the more readable from of primitive call such as
<lntegerAddition i j >
Note that only the longer form (using numbers) is recognized at the
command level.
Information about objects
o (not used)
1 Class (one argument) Returns the class of the argument.
2 SuperObject (one argument) Returns the superobject of the ar-
gument.
3 RespondsToNew (one argument) Returns true if the argument
(a class) responds to new.
4 Size (one argument) Returns the size of the argument. Size is the
size of an array or the number of instance variables for a non-
array.
S HashNumber (one argument) Returns a hash value (integer)
based on the argument.
6 SameTypeOfObject (two arguments) Returns true if the two ar-
guments represent the same type of object.
7 Equality (two arguments) Returns true if the two arguments are
equivalent (= =).
8 Debug (various arguments) Set or reset various toggle switches
used during system development.
9 GeneralityTest (two arguments) Return either true or false de-
pending upon the generality of the arguments.
261
\ \ \
> ~ - : -
.,.
262 Appendix 4 Primitives
Integer manipulation
In all cases there should be only two arguments, of which both must be
integers.
10 IntegerAddition Return the integer sum of the two arguments.
11 IntegerSubtraction Return the integer difference.
12 IntegerLessThan Return true if the first argument is less than the
second; false otherwise.
13 IntegerGreaterThan Integer> test.
14 Integer LessThanOrEqual Integer ~ test.
15 IntegerLessThanOrEqual Integer ~ test.
16 IntegerEquality Integer = test.
17 IntegerNonEquality Integer ~ = test.
18 IntegerMultiplication Return the integer product of the two ar-
guments.
19 IntegerSlash Return the integer result of the II operation on the
two arguments.
Bit manipulation and other integer-valued functions
In all cases there should be only two arguments, which must both be
integers.
20 GCD Return the integer greatest common divisor of the two ar-
guments.
21 BitAt Return the bit value (zero or one) of the first argument at
the location specified by the second argument.
22 BitOR Return the bit-wise logical OR of the two arguments.
23 BitAnd Return the bit-wise logical AND of the two arguments.
24 BitXOR Return the bit-wise logical exclusive-or of the two argu-
ments.
25 BitShift Return the first argument shifted by an amount given by
the second argument. A positive second argument indicates left
shifting; a negative value indicates right shifting.
26 RadixPrint Return a string representing the first argument printed
in the base given by the second argument.
27 not used
28 IntegerDivision Return the quotient of the integer division of the
two arguments.
29 IntegerMod Return the remainder of the integer division of the
two arguments.
.,.
\
\
\
,.
).
Appendix 4 Primitives 263
Other integer functions
.I,n all cases except for primitive 30 there should be only one integer ar-
gument. For p r i ~ i t i v e 30 the first argument must be integer and the second
argument an array.
30 DoPrimitive (two arguments) Return the result of executing the
primitive given by the first argument using the values given in the
array provided by the second argument as arguments for the prim-
itive.
31 not used
32 RandomFloat Converts an integer value into a number in the range
0.0 to 1.0. Used to convert a random integer into a random floating
point value.
33 BitInverse Return the logical bit-wise inverse of the argument.
34 HighBitRetum the position of the first one bit in the argument.
Returns nil if no bit is one in the argument.
~ S Random Using the argument value as a seed, return a random
integer.
36 IntegerToCharacter Return the argument converted into a char-
acter value.
37 IntegerToString Return the argument converted into a string
value.
38 Factorial Return the factorial of the argument. May return as float
if the argument is too large. See also primitive number 77.
39 IntegerToFloat Return the argument converted into a floating point
value.
Character manipulation
In all cases there must be two-character arguments.
40 not used
41 not used
42 CharacterLessThan Return true if the first argument is less than
the second; false otherwise.
43 CharacterGreaterThan Character> test.
44 CharacterLessThanOrEqual Character ~ test.
45 CharacterGreaterThanOrEqual Character ~ test.
46 CharacterEquality Character = test.
47 CharacterNonEquality Character ~ = test.
48 not used
49 not used
264
\ '. i i
; ~
.,
Appendix 4 Primitives
Character unary functions
In all cases there must be only one argument which must be a character.
50 DigitValue Return the integer value representing the position of
the character in the collating sequence.
51 IsVowel Return true if the argument·is a vowel.
52 IsAlpha Return true if the argument is a letter.
53 IsLower Return true if the argument is a lowercase letter.
54 IsUpper Return true if the argument is an uppercase letter.
55 IsSpace Return true if the argument is a white space character
(space, tab, or newline).
56 IsAlnum Return true if the argument is a letter or a digit.
57 ChangeCase Return the argument with case shifted either from
upper- to lowercase or vice versa.
58 CharacterToString Return the argument converted into a string.
59 CharacterTolnteger Return the argument converted into an in-
teger.
Floating point manipulation
In all cases there must be two arguments, both instances of class Float.
60 FloatAddition Return the floating point sum of the two arguments.
61 FloatSubtraction Return the floating point difference of the two
arguments.
62 FloatLessThan Floating point < test.
63 FloatGreaterThan Floating point> test.
64 FloatLessThanOrEqual Floating point ~ test.
65 FloatGreaterThanOrEqual Floating point ~ test.
66 FloatEquality Floating point = test.
67 FloatNonEquaiity Floating point ~ = test.
68 FloatMultiplication Return the floating point product of the two
arguments.
69 FloatDivision Floating point division.
Other floating point operations
In all cases there should be one floating point argument.
70 Log Return the natural log of the argument.
71 SquareRoot Return the square root of the argument.
\
\.
\
,> ) .. ~
;}
Appendix 4 Primitives 265
72 Floor Return the integer floor of the argument.
73 Ceiling Return the integer ceiling of the argument.
74 not used
75 IntegerPart Return the integer portion of the argument.
76 FractionalPart Return the fractional portion of the argument.
77 Gamma Return the value of the gamma function at the argument.
78 FloatToStririg Return the argument converted into a string.
79 Exponent Return the value e raised to the argument.
Other numerical functions
With the exception of primitives 88 and 89, there should be onlyone floating
point argument given to the following primitives.
80 NormalizeRadian Return the argument normalized to between
oand 21T. Normalization is performed by adding or subtracting
multiples of 21T.
81 Sin Return the value of the sine function on the argument.
82 Cos Return the value of the cosine function on the argument.
83 not used
84 ArcSin Return the value of the arc-sine function on the argument.
85 ArcCos Return the value of the arc-cosine function on the argu-
ment.
86 ArcTan Return the value of the arc-tangent function on the ar-
gument.
87 not used
88 Power (two arguments) Return the first value raised to the power
indicatedby the secondargument. Botharguments must be floating
point values.
89 FloatRadixPrint (two arguments) Return a string representation
of the first argument in the base given by the second argument.
The first argument must be float; the second, an integer between
2 and 36.
Symbol Commands
90 not used
91 SymbolCompare (two arguments) Returns true if the arguments
represent the same symbol; false otherwise.
92 SymbolPrintString (one argument) Returns the argument con-
verted into a string.
266
93
94
95
96
97
98
99
'Appendix 4 Primitives
SymbolAsString (one argument) Returns the argument converted
into a string without the leading sharp sign.
SymbolPrint (one or two arguments) Print the symbol after first
indenting an amount specified by the second argument. Second
argument, if given, must be an integer.
not used
not used
NewClass (eight arguments) Return a new object of class Class
initialized with the argument values. Arguments are class name,
superclass name, instance variables, messages, methods, context
size.
InstallClass (two arguments) Insert an object into the internal
class dictionary. First argument must be a symbol (name of class);
second argument is class definition.
FindClass (one argument) Search for an object in the internal
class dictionary. Argument is a symbol representing the class
name.
String operations
100 String Length (one argument) Return an integer representing the
length of the argument string.
101 StringCompare (two arguments) String comparison with case dis-
tinction. Returns either - 1,0, or 1 depending upon whether the
first argument is less than, equal to, or greater than the second.
102 StringCompareWithoutCase (two arguments) String comparison
without case distinction. Returns either true or false depending
upon whether the two arguments are equal.
103 StringCatenation (any number of arguments) Return a new string
formed by catenating the argument strings together.
104 StringAt (2 arguments) Return the character found at the position
in the string indicated by the second argument.
105 StringAtPut (three arguments) At the position given by the second
argument in the string, insert the character given by the third ar-
gument.
106 CopyFromLength (three arguments) Starting at the position given
by the second argument in the string, return the substring of length
given by the third argument.
107 StringCopy (one argument) Return a new string identical to the
argument string.
108 StringAsSymbol (one argument) Return the argument converted
into a symbol.
\
,">:,
Appendix 4 Primitives
\
.">:.
267
109 StringPrintString Return the argument string with quote marks
appending to the edges.
Array manipulation
110 NewObject (one argument) Return an untyped object of the given
size. Argument must be a positive integer. Untyped objects are used
during system bootstrapping.
111 At (two arguments) Return the value found at the given location
in the argument. Second argument must be a positive integer.
112 AtPut (three arguments) At thelocation given by the second ar-
gument, place the value given by the third argument.
113 Grow (two arguments) Return a new object with the same instance
variables as the first argument but with the second argument added
to the end. The argument is usually an array.
114 NewArray (one argument) Return a new instance of Array of the
given size. Differs from primitive 110 in that the object is given
class Array.
115 NewString (one argument) Return a new string of given size. Val-
ues are all blank.
116 NewByteArray (one argument) Return a new ByteArray of the
given size. Values are random.
117 ByteArraySize (one argument) Return an integer representing the
size of the ByteArray argument.
118 ByteArrayAt (two arguments) Return the integer value of the
ByteArray at the given location. Second argument must be a valid
index for the ByteArray given by the first argument.
119 ByteArrayAtPut (three arguments) At the location given by the
second argument, place the value given by the third argument. First
argument must be a ByteArray. Second and third arguments must
be integer.
Output and error messages
120 PrintNoReturn (one argument) Display the argument, which must
be a string, on the output with no return.
121 PrintWithReturn (one argument) Display the argument, which
must be a string, on the output followed by a return.
122 Error (two arguments) Display a message on the error output. First
argument is the receiver; second is a string. The class of the receiver
will be printed, followed by the string.
123 ErrorPrint (one argument) Display a string on the error output.
268
\
~ ~ - -
Appendix 4 Primitives
124
125
126
127
128
129
not used
System (one argument) Execute the Unix system( ) call using the
argument as value.
PriolAt (three arguments) Print a string at a specific point on the
terminal. Second and third arguments are integer coordinates.
BlockReturn (one argument) Issue an error message that a block
return was attempted without the creating context being active.
ReferenceError (one argument) A reference count was detected
that was less than zero. A system error.
DoesNotRespond (two arguments) Print a message indicating that
an attempt was made to send a message to an object that did not
know how to respond to it. First argument is object to which mes-
sage was sent; second argument is message.
File operations
In all cases the first argument must be an instance of class File.
130 FileOpen (three arguments) Open the named file. Second argu-
ment is file name, as a symbol. Third argument is mode, as a string.
131 FileRead (one argument) Return the next object from the file.
132 FileWrite (two arguments) Write the object given by the second
argument onto the file. Argument must be appropriate for mode
of file.
133 FileSetMode (two arguments) Set the file mode. Second argument
is mode indicated, an integer.
134 FileSize (one argument) Compute the size of the file in bytes.
135 FileSetPosition (two arguments) Set the address of the file to the
position given by the second argument, a positive integer.
136 FileFindPosition (one argument) Return an integer representing
the current position in the file.
137 not used
138 not used
139 not used
Process management
140 BlockExecute (one argument) The argument, which must be a
block, is started executing. This primitive cannot be executed via
a doPrimitive: command.
141 NewProcess (one or two arguments) The first argument must be
a block. If the second argument is given, it must be an array of
\
~ .
~ . \.
..~
.,
.'
Appendix 4 Primitives 269
arguments to be used as parameters to the block. A new process
is created that will execute the block.
142 Tenninate (one argument) The argument must be a process. It is
terminated. .
143 Perform (two arguments) The first argument is a symbol repre-
senting the message to be sent. The second argument is an array
of values to be used in performing the message. The first element
of this array is the receiver of the message. This primitive cannot
be executed via a doPrimitive: command.
144 not used
145 SetProcessState (two arguments) The first argument must be a
process. The state of the process is set to that given by the second
argument, an integer.
146 ReturnProcessState (one argument) The argument must be a
process. An integer is returned indicating the current state of the
process.
148 StartAtomic (no arguments) Begin executing atomic actions.
While executing in this mode, no new processes will be started.
Thus the current process can execute uninterruptedly.
149 EndAtomic (no arguments) End executing atomic actions.
Operations on classes
In all cases the first argument must be an instance of class Class.
150 ClassEdit (one argument) Place the user in an editor, editing the
description of the given class. When the user exits the editor, the
class description will automatically be reparsed and included.
151 SuperClass (one argument) Return the superclass of the argument
class.
152 ClassName (one argument) Return a symbol representing the
name of the argument class.
153 ClassNew (one argument) Return a new instance of the given class.
154 PrintMessages (one argument) List all the commands to which
the class responds.
155 RespondsTo (two arguments) Second argument must be a symbol.
Return true if the class responds to the message represented by
the second argument.
156 ClassView (one argument) Place the user in an editor, editing the
description of the given class. Changed class is not included when
the user exits.
157 ClassList (one argument) List all subclasses of the given class.
\
~ .
\
~ \ . - ~
"
270
158
159
Appendix 4 Primitives
Variables (one argument) Return an array of symbols representing
the names of instance variables for the given class.
not used
Date and Time, Terminal Manipulation
160 CurrentTime (no arguments) Return a string representing the cur-
rent date and time.
161 TimeCounter (no arguments) return an integer that is counting
as a seconds time clock.
162 Clear (no arguments) Clear the user's screen.
163 GetString (no arguments) Return text typed at the terminal as a
String.
164 StringAslnteger (one argument) Return an integer taken from the
argument string.
165 StringAsFloat (one argument) Return a floating point value from
the argument string.
Plot(3) Interface
These primitives are effective only if the Little Smalltalk system was con-
figured using the plot(3) interface and if the user is working on a terminal
that accepts the plot commands. Only the long form of the primitive com-
mand using numbers is recognized. The Unix manual should be consulted
for more information on the plot(3) interface.
170 (no arguments) Clear the screen. Although functionally this dupli-
cates primitive 162, it uses the plot interface rather than the curses
interface.
171 (two arguments) Move the cursor to the location given by the two
integer arguments. (Interface to move(x, y).)
172 (two arguments) Draw a line from the current position to the po-
sition given by the two integer arguments. (Interface to cont(x, y).)
173 (two arguments) Draw a point at the location given by the two
integer arguments. (Interface to point(x, y).)
174 (three arguments) The first two arguments give the center of the
circle; the third argument, the radius. Draw a circle. (Interface to
circle(x, y, r).)
175 (five arguments) Draw an arc. (Interface to arc(x,y,xO,yO,xl,yl).)
176 (four arguments) Establish the coordinate space for plotting. (In-
terface to space (a,b,c,d).)
177 (four arguments) Draw a line from one point to another. (Interface
to line (a,b,c,d).)
\.
.. ~ -
Appendix 4 Primitives
\.
271
178 (one argument) Print a label at the current location. Argument is
a string. (Interface to label(s).)
179 Establish a line printing type. Argument is a string. (Interface to
linemod(s).) .
'\
)....
Appendix 5
Differences Between Little
Smalltalk and the Smalltalk-
80 Programming System
This appendix describes the differences between the language accepted by
the Little Smalltalk system and the language described in (Goldberg 83).
The principal reasons for these changes are as follows:
size Classes which are largely unnecessary or which could
be easily simulated by other classes (e.g., Association,
SortedCollection) have been eliminated in the interest of
keeping the size of the standard library as small as pos-
sible. Similarly, indexed instance variables are not sup-
ported, since to support them would increase the size of
every object in the system, and they can be easily sim-
ulated in those classes in which they are important (see
below).
portability Classes which depend upon particular hardware (e.g.,
Form, BitBIt) are not included as part of the Little
Smalltalk system. The basic system assumes nothing
more than ASCII terminals.
representation The need for a textual representation for class descrip-
tions required some modifications to the syntax for class
methods. (See Appendix 2.) Similarly, the fact that
classes and subclasses can be separately parsed, in
either order, forced changes in the scoping rules for in-
stance variables.
The following sections describe these changes in more detail.
1. No Browser
The Smalltalk-80 Programming Environment described in (Goldberg 83)
is not included as part of the Little Smalltalk system. The Little Smalltalk
system is designed to be little, easily portable, and to rely on nothing more
than basic terminal capabilities.
272
Appendix 5 Differences Between Little Smalltalk and Smalltalk 80 Programming System 273
2. Internal Representation Different
The internal representations of objects, including processes, interpreters,
and bytecodes in the Little Smalltalk system is entirely different from the
Smalltalk-80 system described in (Goldberg 83).
3. Fewer Classes
Many of the classes described in (Goldberg 83) are not included as part
of the Little Smalltalk basic system. Some of these are not necessary be-
cause of the decision not to include the editor, browser, and so on, as part
of the basic system. Others are omitted in the interest of keeping the
standard library of classes small. A complete list of included classes for
the Little Smalltalk system is given in Appendix 3.
4. No Class Protocol
Protocol for all classes is defined as part of class Class. The notion of
metaclasses is not supported. It is not possible to redefine class protocol
as part of a class description; only instance protocol can be.
5. Some Messages Different
Because Little Smalltalk does not support class messages (the redefinition
of class protocol to provide messages specific to certain class descriptions),
some actions, such as those dealing with processes, must be performed
differently in Little Smalltalk. Thus the semantics of a few messages have
been changed from those described in the Smalltalk-80 reference book.
These messages have been marked in Appendix 3. The Smalltalk-80 user
should refer to the reference manual for that system for information con-
cerning the way these messages are interpreted.
6. Cascades Different
The semantics of cascades has been simplified and generalized. The
result of a cascaded expression is always the result of the expression to
the left of the first semicolon, which is also the receiver for each subsequent
continuation. Continuations can include multiple messages. Arather non-
sensical, but illustrative, example is the following:
2+3;-7+3;*4
The result of this expression is 5 (the value yielded by 2 + 3); 5 is also the
receiver for the message - 7, and that result ( - 2) is, in tum, the receiver
for the message + 3. This last result is thrown away. The value 5 is then
used again as the receiver for the message * 4, the result of which is also
thrown away.
'. \ \
\.
~ \ :
.'
274 Appendix 5 Differences Between Little Smalltalk and Smalltalk 80 Programming System
In the Smalltalk-80 system a cascaded message. expression is not ail
expression; rather it can be used only as a statement. Also, the receiver
for the continuation portions is not the expression to the left of the first
semicolon; it is the receiver of the last message in that expression. Con-
tinuations can have only one message. Finally, since the cascaded message
expression is not an expression, it is meaningless to ask what the result
should be.
The nonsensical expression presented above would not be legal in the
Smalltalk-80 language; however, the following (equally nonsensical)
would:
2 + 3; - 7 ; * 4
The message 2 + 3 would be evaluated and the result thrown away.
The receiver for that message, namely the 2, would be used as the receiver
for the continuation - 7. The result of that expression would also be
thrown away. Finally the same receiver, 2, would be used for the contin-
uation *4.
In either form, a cascade tends to be used only to combine Creation
and initialization messages. The Little SmaJltalkversion has the advantage
that it can also be used as an expression.
7. Instance Variable Name Scope
In the language described in (Goldberg 83), an instance variable is known
not only to the class protocol in which it is declared but is also valid in
methods defined for any subclasses of that class. In the Little Smalltalk
system an instance variable can be referenced only within the protocol for
the class in which it is declared.
8. Indexed Instance variables
Implicitly defined indexed instance variables are not supported. In any
class for which these variables are desired, they can be easily simulated
by including an additional instance variable containing an array and in-
cluding the following methods;
Class Whatever
J index Vars I
[
new: size
indexVars < - Array new: size
at: location
tindexVars at: location
"\
.."$:,
Appendix 5 Differences Between Little Smalltalk and Smalltalk 80 Programming System 275
at: location put: value
indexVars at: location put: value
The message new: can be used with any class with an effect similar to
new. That is, if a new instance of the class is created by sending the message
new: to the class variable, the message is immediately passed on to the
new instance, and the result returned is used as the result of the creation
message.
9. No Pool Variables I Global Variables
The concepts of pool variables, global variables, or class variables are not
supported. In their place there is a new pseudo-variable, smalltalk, which
responds to the messages at: and at:put:. The keys for this collection can
be arbitrary. Although this facility is available, its use is often a sign of
poor program design and should be avoided.
In the Smalltalk-80 system, an undeclared identifier in a class descrip-
tion is treated as a global variable. In Little Smalltalk, it is an error.
10. No Associations
The class Dictionary stores keys and values separately rather than as
instances of Association. The class Association and all messages referring
to instances of this class have been removed.
11. Generators in place of Streams
The notion of stream has been replaced by the slightly different notion of
generators, in particular the use of the messages first and next in subclasses
of Collection. External files are supported by an explicit class File.
12. Primitives Different
Both the syntax and the use of primitives has been changed. Primitives
provide an interface between the Little Smalltalk world and the underlying
system, permitting the execution of operations that cannot be specified in
Smalltalk. In Little Smalltalk, primitives cannot fail and must return a
v a l u ~ (although they may, in error situations, print an error message and
return nil). The syntax for primitives has been altered to permit the spec-
ifications of primitives with an arbitrary number of arguments. There are
two forms of primitive call. In a class description certain names are rec-
ognized for primitives. Thus a primitive can be written by giving the prim-
itive name followed by the list of arguments, surrounded by angle brackets,
as in:
\
.'.
276 Appendix 5 Differences Between Little Smalltalk and Smalltalk 80 Programming System
<lntegerAddition i j >
The second form of primitives works both at the command level and in
class descriptions. Using this form, the primitive is specified using a num-
ber, as in:
<primitive number argumentlist>
Where number is the number of the primitive to be executed (which must
be a value between 1 and 255), and argu111entlist is a list of Smalltalk
primary expressions. (See Appendix 2.) Appendix 4 lists the meanings of
each of the currently recognized primitive numbers.
13. Byte Arrays
A new syntax has been created for defining an array composed entirely of
unsigned integers in the range 0 to 255. These arrays, instances of class
ByteArray, are given a very concise encoding. The syntax is a pound sign,
followed by a left square brace, followed by a sequence of numbers in the
range 0 to 255, followed by a right square brace.
#[ numbers]
Byte arrays are used extensively internally.
14. New Pseudo Variables
In addition to the pseudo variable smalltalk already mentioned, another
pseduo variable, selfProcess, has been added to the Little Smalltalk sys-
tem. The variable selfProcess returns the currently executing process,
which can then be passed as an argument to a semaphore or be used as
a receiver for a message valid for class Process. Like self and super,
selfProcess cannot be used at the command level.
The global variable Processor and the class ProcessorScheduler are
not included in the Little Smalltalk system.
15. No Dependency
The notions of dependency and automatic dependency updating are not
included in the Little Smalltalk standard library.
Index
== message 18,226
@ message 23, 233
& message 229
\ (continuations) 20
I message 19, 229
- - message 18, 226
A
abstract data types 9
abstract superclasses 9, 32
AbstractGenerator class 86
Active processor state 112, 163
actors 10
Ada 9
add: message 24,243
addFirst: message 24, 251
addLast: message 24,251
Algol-60 19
Alphard 91
and: message 29
arguments of a message 15
Array class 14, 24, 28, 254
ArrayedCollection class 14,253
asArray message 24,240
asBag message 24,240
asFloat message 8, 23, 233
asSet message 24, 240
Association class 275
associations 275
at: message 24,244
at:put: message 24, 244
B
Bag class 24, 27, 242
base of a number 13
basic classes 22
binary messages 15
BinaryTree class 41
Birtwistle, Graham M 9, 193
277
Block class 19, 257
block creation 185
block, internal representation 190
Blocked processor state 113, 164
Boolean class 29,229
bootstrapping 144
browser, Smalltalk-80 .272
ByteArray class 28,254
bytecode interpreter 176
bytecode virtual machine 132
cascades 52, 273
c
Cg 91, 193
Char class 13, 231
CharacterForm class 99
characters 13
class 7
Class class 30, 131, 258
class definition 34, 39
class heading 35
class management 30
class message 18, 226
class name identifiers 14
class parser 172
class/subclass hierarchy 7
CLU 9,91
coerce: message 56, 236, 241
coersions 56
collect: message 24, 26, 241
Collection class 24, 239
collections 24
comments, syntax of 20
Context class 133
continuations 20
control structures 28
coroutines 111
cos message 23,237
courier 136, 184, 189
creating global instance variables .209
278
critical sections 115
critical: message .252
curses terminal package 97
D
Dahl, Ole-Johan 9, 193
date message 31, 246
Demos 72
dependencies 276
detect: message 25, 241
detect:ifAbsent: message 25, 241
Deutsch, L. Peter 92, 111
. Dictionary class 24, 27, 245
Dijkstra, E. W 122, 193
dining philosophers problem 116
DiningPhilosophers class 121
DiscreteProbability class 64
do: message 26, 226
driver process 164, 170
Dynabook pr'O,ject 10, 96
E
editor, for class definitions ; 39
eight queens problem 81
environments, saving .49
Euclid 9
events, simulation 60
F
FactorFilter class 79
False class 29, 230
false pseudo variable 14, 29, 230
File class 27, 252
filters 79
first message 25, 75, 241
flavors 10
Flex system 10, 195
Float class 8, 13, 55, 236
fork message 32, 113, 257
forms, graphics 98
free lists 144
G
garbage collection 144, 206
Generator class 86
Index
generators 38, 75
generators, cross product 85, 90
generators, dot product 85, 88
generators, operations on 84
generators, shuffle 85, 88
getString message 46, 246
global variables ~ 275
goal directed evaluation 81
Goldberg, Adele 10, 72, 194
GPSS 72
graphics, bit mapped 106
graphics, character 97
graphics, line 102
H
Hanson, David R 91, 195
Hewit, Carl 10, 195
history of object oriented programming 9
I
Icon 91, 194
Identifiers 14
ifFalse: message 29, 230
ifIrue: message 29, 230
includes: message 25, 241
indexed instance variables 274
Ingalls, DanieL 5, 19
inheritance 5, 8
initialization of new objects 30
inject:into: message 26, 241
instance variables 14
Integer class 8, 13,55,234
interpreter 176
Interpreter class 133
Interval class 27, 30, 249
invoking the system 209
isKindOf: message 19, 227
isMemberOf: message 18, 227
K
Kay, Alan 10,96, 195
KeyedCollection class 243
keyword messages 15
Knuth, Donald 72, 195
\
\
\
~ ~ ~ \
~ \
Index 279
L
last message 25
Line class l 04
LineForm class 105
List class 24, 27, 250
literal objects 13
LOGO 102, 193
LRG, Learning Research Group 10, 96
M
Magnitude class 8, 13,230
mailbox 1i 1
maxtype: message ., 56,233
memory manager 136, 144
message binding to a method 37
message pattem 36
messages 5
messages, syntax 14, 216
method 5,8
method body : 36
method descriptions .35
methods, internal representation 156
methods, optimization 157
Modula 9
monitors 115
multiple inheritance 207
N
new message 30,259
new message, special processing 37
newProcess message 32, 111, 257
next message 28, 75, 241
nil pseudo variable , 14, 227
Normal class 70
normal distribution 69, 72
not message 29, 230
Number class 8, 13,23, 55,233
numbers 54
numerical generality .55
o
Object class 7, 23, 226
objects 5
objects, internal representation of. 137
objects, memory management 144
obj ects, special 141
optimizations, of bytecodes 157, 159
optimizations, of memory management 148
or: message 29, 229
orthogonal classification 11, 207
overriding of messages 9
p
Pen ciass 102
perform:withArguments: message 32, 247
Peterson, .J. L 116, 196
Philosopher class 119
plot(3) graphics routines 102
Point class 23, 28, 238
pool variables .275
port , 111
prime number generator 76
primitive handler 136, 189
primitives 53, 261
print message 24, 227, 256
printAt: message 98, 256
printNoReturn message 46, 256
printString message 24, 45, 227
Process class 32, 133, 259
process manager 161
processor states 112, 163
producer consumer relationship 110
pseudo variables 14, 276
R
Radian class 23, 237
Random class 28, 239
rapid prototyping 43
ready processor state 164
receiver 5, 15
recursive generators 78
reference counting 144, 206
reject: message : 26, 241
remove: message 25, 241
removeFirst message 25, 251
removeKey: message 25, 244
removeLast message 25,251
respondsTo: message 18, 227, 259
respondTo message 259
response 5
resume message 113, 260
280
Robson, David 194
running the system 209
runningProcess 162
s
scope of instance variables 274
select: message 24, 26, 241
self pseudo variable 14, 37
selfProcess pseudo variable 14, 37, 113
Semaphore class 114, 451
Seque 92, 195
SequenceableCollection class 247
Set class 27, 242
Shaw, Mary 9, 196
signal message 115, 252
Silberschatz, A. 116, 196
Simula 9, 72
Simulation of ice cream store 60
simulations 59
sin message 23, 237
SL5 91, 195
Smalltalk class 31, 246
smalltalk pseudo variable 14, 31, 246
Smalltalk-72 10, 194
Smalltalk-76 10, 195 -
Smalltalk-80, differences between
Little Smalltalk and 15, 52, 272
Snobol-4 91,194
special bytecode instructions 186
special obj ects .- 141
state message 113
Stencil class 105
stream, smalltalk-80 92, 275
String class 14, 23, 28, 255
subclass 7
super object. 138
super pseudo variable 14, 37
superclass 7
superClass message 18
Suspended processor state 112, 164
Symbol class 14,228
syntax of blocks 19
syntax of comments 20
syntax of identifiers 14
Index
syntax of instance variables 35, 214
syntax of messages 14, 216
syntax of methods 35, 213
syntax of objects 12
T
tag, identifier 129
temporary identifiers 36
terminate message 113
Terminated processor state 113, 164
thunk, Algol-60 19
time: message 31, 247
timesRepeat: message 29, 235
to: message 27, 235
to:by: message 27, 235
True class 29, 229
true pseudo variable 14, 29, 229
u
unary messages 15
unblocked processor state 164
UndefinedObject class 227
v
value: message 19, 258
virtual machines 132
w
wait message 115,252
whileTrue: message 30, 258
Wulf, William 10, 197
x
x message 23, 238
y
y message 23, 238
yield message 113, 260

We would like to thanks Tim Budd and his publisher to let us have this book available.

A Little Smalltalk has been scanned and prepared by A. Leinhard, L. Reenggli and S. Ducasse. Note that the book does not have been OCRed because of lack of time. If you are in the mood to do it, please us. You have to pay attention about the following copyright notice. Note that it was not possible to add this notice to all the pages as footnote for obvious technical reasons.

Pearson Education, Inc. reserves the right to take any appropriate action if you have used our intellectual property in violation of any of the requirements set forth in this permissions letter.is hereby granted permission to use the material indicated in the following acknowledgement. This acknowledgement must be carried as a footnote on every page that contains material from our book:LITTLE SMALLTALK by Timothy Budd. Reproduced by permission of Pearson Education, Inc. © 1987 Pearson Education, Inc. All rights reserved. This material may only be used in the following manner: To post the entire book to the following website http://www.iam.unibe.ch/~ducasse/webpages/freebooks.html. This permission is only given on the understanding that access to the material on your website is free to users. You agree to place a link from this book to http://www.aw.com/catalog/academic/discipline/1,4094,69948,00.html. Permission to post this material to your website will expire on January 1st, 2007. If you wish to continue to post the material after that date you will need to reapply for permission referencing this letter. Permission is also given for the entire book to be included on a CD-ROM. This CD-ROM permission is only given on the understanding that it will not be sold for profit. Any monies received from the CDROM must only be used to cover expenses in its production.This permission is non-exclusive and applies solely to publication in ONE CD-ROM EDITION and posted to the website found at http://www.iam.unibe.ch/~ducasse/webpages/freebooks.html in the following language(s) and territory:Language(s): English Territory: World NOTE: This permission does not allow the reproduction of any material copyrighted in or credited to the name of any person or entity other than the publisher named above. The publisher named above disclaims all liability in connection with your use of such material without proper consent.

"\

\

\'>

\

:\

Library of Congress Cataloging-in-Publication Data Budd, Timothy. A Little SmaIItalk. Includes index. 1. Electronic digital computers-Programming. 2. Little SmaIItalk (Computer system) I. Title. QA76.6.B835 1987 005.26 86-25904 ISBN 0-201-10698-1

Reprinted with corrections April, 1987
Copyright © 1987 by Addison-Wesley Publishing Company, Inc. All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, electronic, mechanical, photocopying, recording, or otherwise, without prior written permission of the publisher. Printed in the United States of America. Published simultaneously in Canada. BCDEFGHIJ-DO-8987

Preface
The Little Smalltalk System: Some History
In the spring of 1984 I taught a course in programming languages at the University of Arizona. While preparing lectures for that course, I became interested in the concept of object-oriented programming and, in particular, the way in which the object-oriented paradigm changed the programmers approach to problem solving. During that term and the following summer I gathered as much material as I could about objectoriented programming, especially items relating to the Smalltalk-80 programming system I developed at the Xerox Palo Alto Research Center (Xerox PARC). However, I continued to be frustrated by my inability to gain experience in writing and using Smalltalk programs. At that time the only Smalltalk system I was aware of was the original system running on the Dorado, an expensive machine not available (at that time) outside of Xerox PARCo The facilities available to me consisted of a VAX-780 running Unix2 and conventional ASCII terminals. Thus, it appeared that my chances of running the Xerox Smalltalk-80 system, in the near term, were quite slim; therefore, a number of students and I decided in the summer of 1984 to create our own Smalltalk system. In the fall of 1984 a dozen students and I created the Little Smalltalk system as part of a graduate level seminar on programming language implementation. From the outset, our goals were much less ambitious than those of the original developers of the Smalltalk-80 system. While we appreciated the importance of the innovative concepts in programming environments and graphics pioneered by the Xerox group, we were painfully aware of our own limitations, both in manpower and in facilities. Our goals, in order of importance, were:

o The new system should support a language that is as close as possible
to the published Smalltalk-80 description (Goldberg and Robson 83).

o The system should run under Unix using only conventional terminals.
o o
The system should be written in C and be as portable as possible. The system should be small. In particular, it should work on 16-bit machines with separate instruction and data spaces, but preferably even on those machines without this feature.

I. Smalltalk-80 is a trademark of the Xerox Corporation. 2. Unix is a trademark of AT&T Bell Laboratories.

v

\ .,

\

\
:}o:

.'

vi

Preface

In hindsight, we seem to have fulfilled our goals rather well. The language accepted by the Little Smalltalk system is close enough to that of the Smalltalk-80 programming system that users seem to have little difficulty (at least with the language) in moving from one system to the other. The system has proved to be extremely portable: it has been transported to a dozen varieties of Unix running on many different machines. Over 200 sites now use the Little Smalltalk system.
About A Little Smalltalk

This book is divided into two parts. The first section describes the language of the Little Smalltalk system. Although most readers probably will have had some prior exposure to at least one other programming language before encountering Smalltalk, the text makes no assumptions about background. Most upper division undergraduate or graduate level students should be able to understand the material in this first section. This first part of the text can be used alone. The second part of the book describes the actual implementation of the Little Smalltalk system. This section requires the reader to have a much greater background in computer science. Since Little Smalltalk is written in C, at least a rudimentary knowledge of that language is required. A good background in data structures is also valuable. The reader will find it desirable, although not strictly necessary, to have had some introduction to compiler construction for a conventional language, such as Pascal.
Acknowledgments

I am, of course, most grateful to the students in the graduate seminar at the University of Arizona where the Little Smalltalk system was developed. The many heated discussions and insightful ideas generated were most enjoyable and stimulating. Participants in that seminar were Mike Benhase, Nick Buchholz, Dave Burns, John Cabral, Clayton Curtis, Roger Hayes, Tom Hicks, Rob McConeghy, Kelvin Nilsen, May Lee Noah, Sean O'Malley, and Dennis Vadner. This text grew out of notes developed for that course, and includes many ideas contributed by the participants. In particular I wish to thank Dave Burns for the original versions of the simulation described in Chapter 7 and Mike Benhase and Dennis Vadner for their work on processes and the dining philosophers solution presented in Chapter 10. Discussions with many people have yielded insights or examples that eventually found their way into this book. I wish to thank, in particular, Jane Cameron, Chris Fraser, Ralph Griswold, Paul Klint, Gary Levin, and Dave Robson. Irv Elshoff provided valuable assistance by trying to learn Smalltalk from an early manuscript and by making many useful and detailed comments on the text.

Obtaining the Little Smalltalk System The Little Smalltalk system can be obtained directly from the author. Oregon 97331 USA . Frances Van Scoy from West Virginia University provided careful and detailed comments on earlier drafts of the book.1~ \ ~\ \ \ " Preface. David Robson from Xerox Palo Alto Research Center. Davis from Iowa State University. Jan Gray at Waterloo and Charles Hayden at AT&T were early non-Arizona users of Little Smalltalk and were extremely helpful in finding bugs in the earlier distributions. vii J. and. for helping to make the department such a pleasant place to work. The distribution tape includes all sources and on-line documentation for the system. A. including cost. Finally I wish to thank Paul Vitanyi and Lambert Meertens for providing me with the chance to work at the Centrum voor Wiskunde en Informatica in Amsterdam for the year between my time in Arizona and my move to Oregon. I wish to thank Ralph Griswold. Charlie Allen at Purdue. Dave Hanson. write to the following address: Smalltalk Distribution Department of Computer Science Oregon State University Corvallis. all chairmen of the computer science department at the University of Arizona at various times in the last five years. and Chris Fraser. and for permitting me to finish work on the book while there. Paul Klint from the CWI. For further information on the distribution. The system is distributed on 9-track tapes in tar format (the standard unix distribution format).

':':.\ . \- .\ >-. \ .

................ Background Reading......................... method...................... 9 This chapter introduces the basic concepts of the Smalltalk language........................ and Inheritance 5 History.........':)- Table of Contents --ONE The Language =CHAPTER PAR T 1 1 3 Basics............................................ Objects.................................... Getting Started Finding Out About Objects ix ...................................... namely object..... =CHAPTER 2 12 13 14 15 17 18 Syntax Literal Constants Identifiers................................... class.................................................................... Messages. inheritance and overriding........... Classes............

........... CHAPTER 5 A Simple Application......... It explains how to use the Little Smalltalk system to evaluate expressions typed in directly at the keyboard and how to use a few simple messages to discover information about different types of objects...............................................")...... 30 Abstract Superclasses 32 The basic classes included in the Little Smalltalk standard library are explained in this chapter.... ~ \> ~~ \............................................. 22 Basic Objects 23 Collections 24 Control Structures 28 Class Management.Example 37 Processing a Class Definition 39 This chapter introduces the syntax used for defining classes......... ................. ....... ~\ x Contents Blocks 19 Comments and Continuations 20 This chapter introduces the syntax for literal objects (such as numbers) and the syntax for messages.................................................................. CHAPTER 3 Basic Classes............ An example class definition is presented................................. 34 An Illustrative ... 42 Saving Environments 49 This chapter illustrates the development of a simple application in Smalltalk and describes how environments can be saved and restored.... CHAPTER 4 Class Definition................

................... CHAPTER 8 74 Generators Filters 79 81 Goal-Directed Evaluation Operatkms on Generators 84 91 Further Reading This chapter introduces the concept of generators and shows how generators can be used in the solution of problems requiring goaldirected evaluation...................................... illustrating the ease with which simulations can be described in Smalltalk.... 59 The Ice Cream Store Simulation 60 Further Reading 72 This chapter presents a simple simulation of an ice cream store...................... It illustrates the use of primitives by showing how primitives are used to produce the correct results for mixed mode arithmetic operations................................................ Cascades...............Contents xi -CHAPTER 6 51 Primitives.. 97 102 106 Graphics..... ::=CHAPTER 7 A Simulation........................................ and Coercions Cascades..... =CHAPTER 9 ...................................... 95 Character Graphics Line Graphics : Bit-Mapped Graphics .............. 52 Primitives 53 Numbers 54 This chapter introduces the syntax for cascaded expressions and describes the notion of primitive expressions........

PAR T TWO The Implementation CHAPTER 125 11 127 129 129 131 132 135 ~ Implementation Overview Identifier Typelessness Unscoped Lifetimes An Interactive System A Multi-Processing Language System Overview This chapter describes the features that make an interpreter for the Smalltalk language different from. . -CHAPTER 10 109 114 115 116 122 Processes Semaphores Monitors The Dining Philosophers Problem Further Reading This chapter introduces the concepts of processes and semaphores. ~\ \ \ ~\ :~ \~ xii Contents Although graphics are not fundamental to Little Smalltalk in the same way that they are an intrinsic part of the Smalltalk-80 system. a Pascal compiler. say. Provides a high-level description of the major components in the Little Smalltalk system. It illustrates these concepts using the dining philosophers problem."\:. it is still possible to describe some graphics functions using the language. This chapter details three types of approaches to graphics.

156 157 159 Bytecodes The Representation of Methods Optimizations Dynamic Optimizations The techniques used to represent methods internally in the Little Smalltalk system are described in this chapter. which also overviews the memory management algorithms. The chapter ends by describing the class parser and the internal representation of classes. the process that reads commands from the user terminal and schedules them for execution.Contents xiii CHAPTER 12 137 141 144 148 1he Representation of Objects Special Objects Memory Management Optimizations The internal representation of objects in the Little Smalltalk system is described in this chapter. It then goes on to describe the driver. The chapter ends with a discussion of several optimizations used to improve the speed of the Little Smalltalk system. CHAPTER 13 150 : . the process manager. CHAPTER 15 176 179 182 The Interpreter Push Opcodes Pop Opcodes . -CHAPTER 14 161 170 172 The Process Manager The Driver The Class Parser This chapter presents a more detailed view of the central component of the Little Smalltalk system.

APPENDIX 2 213 Syntax Charts Presents syntax charts describing the language accepted by the Little Smalltalk system. References 193 An annotated bibliography of references related to the Little Smalltalk system. It ends by describing the primitive handler and the manipulation of special objects. Lists the various options available. APPENDIX 3 22 5 Class Descriptions Presents descriptions of the various messages to which the classes in the standard library will respond. . Projects Appendices APPENDIX 198 1 209 Running Little Smalltalk Describes how to run the Little Smalltalk system." .' xiv Contents Message-Sending Opcodes Block Creation Special Instructions The Courier The Primitive Handler Blocks 182 185 186 189 189 190 This chapter describes the actions of the interpreter and the courier in executing bytecodes and passing messages.

- - .\. APPENDIX 5 272 Differences Between Little Smalltalk and the Smalltalk-80 Programming System Describes the differences between Little Smalltalk and the Xerox Smalltalk-80 systems. Contents xv APPENDIX 4 261 Primitives Gives the meanings of the various primitive numbers.

. ~ ~ ~ '0 ~ ~ : : § ~«««««««««~ :::::: +: ~ / '" V /\ ::It:::+::::::. ~ ~ : : ! § §{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{W~ f : : ~ ..... ~: ~ ~. ® . ~ ~ §§ 3 ~ ~ ." / ~ ! ~ 8 ~ . ~. ::: ~: _ V /\ (8) ~ :+:::::: .-:: :::: of{ % ~. ./ ~ '" / 8 '" -.\ ~ ~ ~ '8' :::: / " 8~ ~ ~ (8'.~ ~ ~ " " " '" " '" " " '"'"'"'"" " " " ""~ ~: ~ @.j( % \S) \'j !~ '" / :: < < < < < < < < < < < < < < < < < < . ! ~ ~ ..) : € ! ~ §// / / / / / / / / / / / / / / / / 2 f : : ~ . /\ !\ i\ 1\ .\ '" / ."""""""""""""""".~ ~ }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}i~ ~: ~ ® 0 I~ ~ ""'" """""'" '"'" '" """"'" "'~ ~: ~ ~. ~ ! § §({{{«{{({{{{«{{«««««{{«««««««I~ ~ i ! ~ . is) V /\ '" / ~ ~~ ~ ~~ ~ ~ ~ ~ . .:.8. .~ :::: ~ § §}}}}}}}}}})}})}}}}}}}}}}}}}}}}}}}}}}}}}}}}})}}}§: :::: @: . ~~ ~ ~ ::':0 ~ ~ . :+::::::::: :+:::::: : ~ ~:««««««««««~. . .~~ ~ ::tf:: ::It:: 'lj. ««««««««««~ :::: :::::: ~ ~ +: ::tf:: ~ ~ > > > »»>> > »»>> > > >:::~ ~: ~ ~. . ~ : § §// / / / / / / / / / / / / / / / / § §: :::::::::: ::: ::::::: ~: iC ~ ::tf:: \S} @j '\~~ V v '\ !\ !\ .V /\ /\ t\ II ".~ ~ ~ j / : § ~»»»»»»»»»»»»»»»»»»»»»)~ §: ~ ~~ 0 . '" / §({«««(((((((((((((((( \S) :::»»»»»»»»»>:::: iC ~ S :: ::: : ~ ~ @: @i V V V . ««««««««««««««««««««««~~: ))))))))))))))))))))))))))))))))))))))))))))§ ~ ~ ~ ~ ~ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{@ §: ~ ®'0 ~ ~ j }}}}}}}}}}}}}}}}}}})}}}}})}}})}}}})}}}}}}}}}}}}}J~ ~ f :::: ~ . ~ ~~ ~ j /~ ~ ~ 0 ~ ~ j §f ! ~ @) V !\ ~ /- 3«« < < ««« < ««< 2»»»>>>>>>>>>>>> 2'-"""""""""""""""" ~/ / ////////////// :::~: ««««««««««~~: ~ '" ///////////////// §: / »»»»»»»»»»§ ~ :/ r t: / '" / " V V V ..~ j ~ ~ j: ® ~ : ~ ~«««««««««««««««««««{««~: ~ @ 0 ~ ~ j § j ~ . ~ ~~ ~ j §»»»»»»»»»»»»»»»»»»»») ::: ~ -Ie: ::tf:: ~@)V ~ V /\ !\ @"{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{i @}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}.. ~ ~ ~ S ! s §f :::::: __ ~ ~ »»»»»»»»» ~ §// / / / / / / / / / / / / / / / ~ ~ '"" " " " '"" " '"'"" '"'"'"" " " / / / / / / / / / / / / / / / / / / / / / / / /~ ~ . »»»»»»»»»»»»»»»»»»»»»).s. . .:\Vfl\"'/ ~ 0 ~ '~ ~ : ~ ~ ~ ~ ~ ~ ~ :: ! ~ j 0 ~~ ~ ~ ~ ~ 0 . "'/'--."'§ ~: ~ §: ::It:: '2. :~ ~: '" .\ '" '" " " ~{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ ~: V V V /\ (\ (\ '" '" ::- §«««««««««««{«{«««««««I ~ »»»»»»»»»»»»»»»»»»»» ::tf:: % ::It:: ~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}Jl _ ~ ~ ~ ~ ~. .»»»»»»»»»>=:.v. §{{{{{{{{{{f{f{{{f{{{{{{{{{{{f{{{f{{{{{{f{{f{{{{{€ §'"'"" '"'"'"" '"'"'"'"" '"'"" " .2 f ! ~ ~ ~.A~ $.~ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{~§.:.\ ~ : : ~ ~ 0 . ~ y ~~ ~ j (« (« «(««((((((((((««(((((((((((((«((: : ::::::.\ .~ ~ ! ! ~ §'..\.~ ~~ :~ : : § ~ »»»»»»»»»»»»»»»»»»»»»~.: :::::: iC // ~ V /\ !~ ~ / '" \j /\ ~~. V 1\ 8 \ f /\ '" " '" / / / @. : j ~ !~~ : ~ ~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}r~ §: ~ ~...

......... .~: -~ it:: :\ 1 '~. ~ "S?' it:: /\ .....-----" -" .... .. "'.". "'.....F::~ <:: <:: <:~ .. .. "'. . ... ---..\....* .....::-".. . .........~ %: ~ * * * * * * * * * * * * * * * * * * * * * . -" .. * ...> :> :> ::> ~:> ~~~ :::~ ::> ::~ :> ~:> ~~..." "'...* .-..* --. % ~ "'. j\ \/ ~v ~~~ ........ . " " " " " " " ...... y "'..: "'<.. "'.- '-"" . "'. ~~~ .\ ~:S\ ::u:: ::u:: ::u:: ::u:: %: %: ~ ~ ...... ---... "-'-....... ' .. ~ V V :\ '- /\ !\ . . '-'.. \1 \/ V /\ /\ !\ --.. ....... ~.. ----... .. "'.r\ ~v /\ :.. .... . ---...." "'..\ !\ ~- "It: " "-"'..I~.* . ....... * ''--'" .---.. -:2~ :~......... ~...-:...~ :-> :.~~:: <"::: <:~ ..... "'..-'.. -- '--'" ))))))))))))))))))))))}}}})))) {{{{{ {{ {{{{{{{ {{{{ {{ {{ {{ {{{{{{{{{ {{{ {{{{ }}}}}}} }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} .. '2.....* . .... ... .* * * * * * * ---* * * . . .> / .." . .. ..'" " '-......> ~::-~ ~':. I .- - ------- ONE The Language --- {{f{{{{{ {-[ {HI{ f{ {-[ {H{{ {{{{ {{ {{ {{{{{{{{ H}}}} n }l-} }}}}}}}l} an}}}}}}}}} n}}}} J "'. \/ !\ !\ !\ 4 \..//// / .. v v v \... / / ' / / / / . it:: % % "'. ....* .. "'.... . ...... it:: ......'" "'.. V \j ~.. V \/ \/ \/ \j !\ ..... . "'......j \! \/ \/ /\ j\ 1\ ~- :~. ....: ....../ \! v V j\ ................. .~ <::..... '- '// / / / / / / / / // // / / :«««««««« »»»»»»»»~ PAR T ..* -'* "-" * --" " " :««««««««««««««({{{««( V ..../ \/ \j \j %: % ::tl:: % '#: '#: ~ ~ ~ !\ . -"" "'.. ..<: . ..... 1\ ~.... '-" "/ / / /.. .. . "'... :::: <. ...... ~..... "-.. ~ :'. ... "'........ \.... * '--'" '--'" >t- ...... /\ /\ /\ /\ !\ %: ~ it:: " .... ~-.. .... ....* * * ..\ -:0 :tl:: ..::: ... ---.* .F... ''-.. . >1'-"' --'-"' '-"" ... .~.... -~.. «({{««««««««({{{{««««« »»»»»»»»»»»»»»»»»» " ..* .~~ \/ v / ..

\ .\ .

}}}}}}}}}}}}}}}}}}}}}}}}1: «({({««««««««(. 1 Basics »»»»» ////////// :{{ {{{{{{{{{{{{ {{{{{{{{{{{ HHHH}HH n}}}}}}}}}}} ((((((((({((\ ))))))))))))))))))))))) ""'-"'-"'-"'-"'-"'-"'-"'-" 3 .\ ~\.- »»»»»> ////////// CHAPTER "'-"'-"'-"'-"'-"'-"'-"'-"'-" {{{{{{{{{{{H{{{{{{{{{{{{ i .

or "pigeon-hole" model. it does little to help us understand how to solve problems using the computer. receiving a copy of my file) is well defined and predictable. She lives is a city many miles away. and pushing the results back into other slots. In this view the computer is a data manager. pulling values out of various slots (memory addresses). but there is no reason for me to know the details of how my directive is implemented as long as the response (the delivery of the flowers. I must know the commands to which the resource will respond (send flowers to my grandmother. is that I do not need. The object-oriented model of problem solving views the computer in much the same fashion as just described. If I persist. that they are obtained in bulk in the morning from a flower wholesaler. following some pattern of instructions. In real life we call this process Udelegation of authority." In computer science it is called l'abstraction" or "information hiding. Most likely the steps the resource must take to respond to my request are much more complex than I realize.4 The Language The traditional model describing the behavior of a computer executing a program is the process-state. however. to know how my simple directive "send flowers to my grandmother" is going to be carried out. many people who have a traditional background in computer . indeed most of the time do not want. transforming them in some manner. There is a resource (a florist. I might inquire further to find out how the florist in mygrandmother's city obtains the flowers and find. By examining the values in the slots one can determine the state of the machine or the results produced by a computation. a file server) that I wish to use. Let us examine a realistic situation and then ask how we might make a computer more closely model the methods people use for solving problems in everyday life. The important point. these terms amount to the same thing. If I investigate. I would probably discover that my florist sends a message describing my order to another florist in my grandmother's city. While this may be a more or less accurate picture of what takes place in a computer. In order to communicate.'1 At the heart. That florist then makes up the arrangement and delivers the flowers. describe the kinds and number of flowers I want sent and I can be assured that they will be automatically delivered. however. Indeed many people who have no training in computer science and no idea how a computer works find the object-oriented model of problem solving quite natural. perhaps. wandering through memory. and it is certainly not the way most people (pigeons and postmen excepted) go about solving problems. I might even be able to follow the chain all the way back to the farmer who grows the flowers. return a copy of the file named "chapter!"). I merely go to a local florist. and discover what requests were made by each member of the chain in order to solicit the desired outcome from the next. Suppose I wish to send some flowers to my grandmother for her birthday. Surprisingly. The task is easy enough to do.

The notion that "7" is an object and" + is a request for an addition. for example. . everything is an object. we have a universe of well-behaved objects that courteously ask each other to carry out their various desires. But soon. or a command to a disk drive). accessible to only that object.Basics 5 programming initially think there is something strange about the objectoriented view. the message my florist sends to the florist in my grandmother's city. Classes. power. I am an object and the flower shop (or the florist in it) is another object. as with LISP. That is.essages) between objects. Actions are initiated by sending requests (or 111. Such anthropomorphic viewpoints are common among Smalltalk programmers. within the language. or a result code) returned directly back to me. may at first seem strange. We will discuss this in more detail later. I transmitted the request "send flowers to my grandmother" to the florist-object. we hope to show how the objectoriented model aids in the creation of large software systems and assists in the solution of many problems using the computer. the receiver may have to transmit other messages to yet more objects (for example.1). or method. the uniformity creates both the simplicity and power of the language. Maybe the receiver can immediately satisfy my request. to satisfy my request. The reaction of the receiver of my message is to execute some sequence of actions. an object's memory can contain only other objects. the uniformity. There is no way to create. In addition. By describing the solution of several problems in Smalltalk. An object possesses several characteristics (Figure 1. and flexibility the object-message metaphor bring to problem solving makes this interpretation seem natural. there is an explicit response (a receipt. an entity that is not an object. On the other hand. in order to meet my needs. Among major computer languages this uniformity in Smalltalk is rivaled perhaps only by LISP. Of course. Objects. and Inheritance In Smalltalk. The Smalltalk universe is inhabited by objects. no object can read or modify memory values in another object. In my flo\ver example. since everything in the system must be an object. and. In subsequent chapters we will see how the Smalltalk language embodies this object-oriented view of programming. Every object contains a small amount of memory. Dan Ingalls describes the Smalltalk philosophy (Byte 81): II Instead of a bit-grinding processor raping and plundering data structures.

each apple is distinct from all others. they will all . who continues execution from the point of call."\ ~\ \ ~:} \.1 D A typical object a class I---~ Methods description description class pointer memory 1-----------1 object pointers other objects All actions in the Smalltalk system are produced by passing messages. Yet certain statements can be made about all the apples. however.. There are two different ways to view this message passing operation. and the relationship between the sender and receiver of a message is typically much more fluid than the static relationship between a caller and the callee in a conventional programming language. The first is simply that message passing corresponds to a subroutine call in a conventional procedural language. and it can contain certain argument values to be used in conjunction with the execution of the requested operation. In the real world every object is individualistic. In a bushel of apples. The result is then returned to the sender. however. objects. similar.' 6 The Language Figure 1. for example.. . A message is a request for an object to perform some operation. This is true in that the actions of the sender ate suspended while the receiver produces a result. each also possesses common characteristics with other. \. for example. Messages can be created dynamically at run-time.-. such as Pascal.

extending fro'm the basic class Object. That is. The same situation holds regarding entities in Smalltalk. down through more and more specific classes until we reach the individual object itself. we can take this analysis even one step further. making Fruit a subclass of a more universal category.2) that illustrates this class-subclass hierarchy. which we can call Object. we can view an apple as an individual item or as an instance of a larger class (or category) of objects. Although 7 and 8 are distinct objects. There is a natural tree structure (Figure 1. and so on. let us denote the class of all apples by Apple. That is. The Class Fruit encompasses both the classes Apple and Orange. to be used when we wish to describe characteristics common to both apples and oranges. the capital letter and the boldface type serving to denote the fact that we are talking about a class. Thus we have a hierarchy of categories for objects. This process is called classification. Thus we say that Fruit is a superclass of Apple and Orange. With the exception of class Object. and that Apple and Orange are in turn subclasses of Fruit. we will denote class names by capitalizing the first letter and denote object names without using the capital. of which everything is a member. that class will. Fruit. and not an individuaL Instances of class Orange are in many respects different from apples. But they also share many common characteristics with apples. the number 7 is an instance of the class Integer. for example. both 7 and 8 will respond to the message "+" with an Figure 1. can all be used to bake pies in a similar manner.2 0 The tree structure of the class-subclass Hierarchy Object FrLiit /~ Orange Vegetable Apple /\ Turnip /\ Carrot . every object is a member of some class. and thus deserve their own category. Finally. as is the number 8. in turn. As we have been doing. which in turn may be part of another class. Thus we can create a new class. up to one single class of which every object is a member. For example.~ - Basics 7 smell and taste' a certain way. they share some characteristics by virtue of their being instances of the same class. So.\ ~. be a subclass of some larger class.

This is the same message that was originally passed to the integer 2. 7 and 8 will respond to the message + " in the same fashion. so that when the Little Smalltalk system tries to find an associated method for the message exp in the class Integer protocol.0. for example. There. Float. . For example. rather every object must be associated with some class. but what about methods associated with superclasses? The answer is that any method associated with a superclass is inherited by a class. in the protocol for Number. Class Integer contains in its protocol. namely Number. we say that the method for exp is inherited by the class Integer from the class Number. 2. 2. of which values such as 3. because they are both instances of class Integer. Number is a subclass of Magnitude (a class to be discussed later) which finally is a subclass of Object. and so on. It is not possible to provide a method for an individual object. For example. Now the class description for Integer does not provide a method for the message exp. In Smalltalk. or approximately 7. Thus." Thus the message asFloat is passed to the original integer." Thus 2 exp yields e 2 . it finds a method and executes it. The behavior of an object in response to a specific message is dictated by that objecfs class. In Number. in class Integer there is a method associated with the message" +. a protocol is provided as part of a class definition.1415926 are instances.71828. If an object is an instance of a particular class. When sent to a number. -. An example will help clarify this concept.} \ ~} \ :. it is clear how methods associated with that class will be used.." The entire set of messages associated with a class is called the protocol for that class. The message exp is then passed to this value. The list of statements that define how an instance of some class will respond to a message is called the method for that message. say.'. There are other subclasses of Number. the message exp means "return the value of e (approximately 2. *. resulting in a floating point value. . for the moment we can translate this as "produce an instance of Float with your value (self asFloat) and send that object the message exp asking for e raised to its value.) raised to your value. it does not find one. The syntax for class definitions will be described in a later section.. So the Little Smalltalk system next examines the protocol associated with the immediate superclass of Integer. messages for +." '( > 8 The Language integer argument by performing integer addition. and the behavior of the object in response to messages will be dictated by the methods associated with that class. the method associated with the message exp is as follows: If i self asFloat exp We will explain the syntax in more detail later. . only now the class of the receiver is Float. Integer is a subclass of a larger class. for example. not Integer. Number.38906. The method associated with this message is executed. Return (the up arrow i indicates returning a value) the response to that message.

As we have seen. Search for a method begins with the class of an object and then proceeds through the various superclasses (along the superclass chain). While Simula allowed users to create object-oriented systems. such as integers and floating point numbers. The fundamental notions of objects. and classes came from the language Simula (Birtwistle 73). will respond in a similar manner in common situations.3 0 The class structure of numbers Object Magnitude I Integer /"" Number /~ Float Char Figure 1. a method for the message exp is defined in both the classes Number and Float. History. Abstract superclasses are important in insuring that instances of different classes. as necessary. they reduce the size of the descriptions needed to obtain a desired behavior. Modula. . messages. the concept of classes led to the development of the notion of modules and abstract data types (Shaw 80).\. and Ada. If a floating point value is given the message exp it will execute the method in class Float. An addition.3 shows the hierarchy representing several classes. CLU. the support for which was a fundamental goal in several languages such as Euclid. not the method in class Number. within a class the response to a message (the Simula equivalent of a method) was still expressed in the standard ALGOL data/procedure-oriented fashion. Within the ALGOL family of languages. Basics 9 Figure 1. including numbers. Background Reading The concepts relating to object-oriented programming found in Smalltalk are the products of a long process of language development and evolution. are known as abstract superclasses. which usually do not have any explicit instances. by eliminating the need to duplicate the methods for the messages in the superclass. Thus the method for exp in Float is said to override the method in class Number. Classes such as Number and Magnitude.

. Koved and LaLonde present overviews describing the object-oriented viewpoint in various guises (Koved 84) (LaLonde 84). SmalltaIk-72 (Goldberg 76). and distinguish those that are found in lower levels but not higher levels. The development of actors in Lisp paralleled the development of Smalltalk. and in the development of languages for animation and graphics (Reynolds 82). DIrect ancestors of Smalltalk include the Flex system (Kay 69). EXERCISES 1. in contrast to Simula.. class descriptions are represented as objects. Define the following terms: object message receiver method protocol class subclass superclass inheritance overriding abstract superclass 2. Similarly. The Smalltalk languages were all produced as part of the Dynabook project initiated by Alan Kay in the Learning Research Group at the Xerox Palo Alto Research Center. This interface becomes almost completely described in object-oriented form in the Smalltalk-80 programming environment (Goldberg. List properties that can be found at each level.\ 10 The Language While the object-oriented philosophy was slowly gaining acceptance in the programming language world.. Such a view is natural and convenient when then the computations may physically be executing on distributed processors. In Smalltalk-76. Give an example of a hierarchy from everyday life. (Almes 85). and Smalltalk-76 (Ingalls 78). in operating systems design the notion of independent computations that interact with each other solely by exchanging messages was finding advocates (Wulf 74).83). numbers and control structures are treated as objects. most notably in the notion of actors (Hewit 73) and flavors (Weinreb 80) in Lisp. similar ideas were gaining acceptance in the architecture community (Pinnow 82). The object-oriented view of programing has also influenced other computer languages. and the object-oriented point of view is extended to the programming interface. The evolution of the language as traced in these documents shows the object-oriented model slowly being expanded to include more and more language concepts.. in Smalltalk-72. A number of papers describing various aspects of the Smalltalk-80 system were included in a special issue of the magazine Byte (Byte 81). but classes are still a special form. .\ . For example. and the two languages had an influence on each other. .' "\ : "\ . \ .

Basics 11 3. The robin is also a North American bird but is not a bird of prey. inefficient. In the real world. Read about the Simula class mechanism (DaW 72) (BirtwistIe 73). Compare and contrast this with the Smalltalk class mechanism. or both. rather than in the tree-like hierarchy of Smalltalk. forcing the classification into a treelike structure is either unnatural. How might Smalltalk objects be classified in orthogonal ways? What problems does this introduce for the inheritance mechanism? How might these problems be overcome? Bird Not a Bird of Prey North American South American Bald Eagle Condor of Prey Robin ? . Thus. but one is a North American bird and the other a South American bird. For example. the bald eagle and the condor are both birds of prey. These two distinguishing characteristics are orthogonal in that neither can logically be said to be a superset of the other. 4. objects are often classified in orthogonal ways.

: /\ !\ CHAPTER / / //' / / 2 Syntax // //// / / / // :{{{{{{{{{{{{{{{{H{{{{{{{ r}}} }}}} }}}} }l}} I}}}} H}}} .:.'.{{{({{««««««««: 12 . " ~\ \ '\.

always denotes the same object. and objects are characterized by the messages they accept and their response to them. and by the fact that they do not have to be declared prior to being used. they can be compared and hence are a subclass of class Magnitude. no matter where it appears. In Smalltalk this distinction is much less distinct. Examples of numbers are: 7 16rFF . Any number can be preceded by a base. The discussion of syntax begins with a description of how objects are represented. For example. The class Char provides capabilities for dealing with character values. might denote an object.1415926 2e32 2. All entities. are objects. Literal Constants Some objects. Thus 7 denotes an object in the same way that an identifier. or just 255. namely integer and floating point values. The following are example instances of this class: .ABC The use of a base is merely for the sake of convenience and appearance. The number 16rFF is the same as the number 10r255. An instance of class Integer consists of an optional sign followed by any number of digits. In Algol-like languages a symbol such as 7 conventionally denotes a "value" rather than an identifier. There are two classes of numbers that can be written as literal objects.4e-32 15rC. such as x (in the proper context). everything in Smalltalk is an object. which is a positive integer followed by the letter t.3. literal objects. As we noted in Chapter 1. including numbers. Characters are distinct from numbers. Numbers are perhaps the most common literal objects. Numbers respond to a variety of arithmetic messages (inherited from class Number) and relational messages (inherited from class Magnitude). A character is written as a dollar sign followed by the character symbol. are distinguished by the fact that their name uniquely identifies the object. For bases greater than 10 the letters A through Z are interpreted as digit characters. A Float consists of an integer followed by a period (the radix point) and another unsigned integer (the fractional part) and/or the lettere and a signed integer (the exponent part). Since characters possess an ordering. independent of context. given by a collating sequence. the symbol 7.Syntax 13 This chapter will describe the way in which objects are represented and manipulated in Little Smalltalk.

The elements in the array list are literal objects (numbers or symbols). Furthermore the same sequence of letters will always denote the same object. Unlike numbers. "\ . A symbol is written as a pound sign (#) followed by any sequence of characters. Examples of Arrays are: #(this is an array of symbols) #(12 label (another array» Arrays and strings use the messages at: and at:put: to select and modify particular elements in their collection. A string is similar to an array.. \ .symbol. Within the array list the leading pound sign on symbols and arrays can be eliminated. .periods Identifiers Identifiers in Little Smalltalk can be divided into three categories: instance variables. Examples of strings are: 'a String l !a String with an embedded quote mark! II An Array is written as a pound sign (#) followed by a list of array elements in parentheses.'-: 14 The Language $A $7 $ $$ An instance of class String is represented by a sequence of characters between single quote marks.with. whereas an identifier beginning with a lowercase letter must represent either a pseudo variable or an instance variable. or other arrays. Example symbols are: #aSymbol #AndAnother #+++ #very.. in fact the class String is a subclass of ArrayedCollection. The class Symbol is another literal class.:. symbols do not possess an ordering and cannot be compared (except for.long . object equality). An identifier beginning with a capital letter is always a class name.. characters. Both strings and arrays can be catenated together to form larger strings by using the comma (.) operator. of course. ~\ \ . Embedding a quote mark within a string requires two adjacent quote marks. strings. Unlike a string (which is also a sequence of characters) a symbol cannot be broken into smaller parts. and pseudo-variables. class names. or strings. as is the class Array. Spaces between characters are not allowed.

and nil. super. the message respondTo. respectively. are defined to be instances (usually the only instances) of the classes True.to represent this two-character sequence. The final pseudo variable. true. Arguments for a method (to be discussed shortly) are also considered to be. they are named by a sequence of letters beginning with a lower case letter). true. where different techniques are used to obtain the currently executing process or to obtain information about the current environment. will cause the class to print a list of the messages to which instances bf the class will respond. self. when passed to an object representing a class. Pseudo variables look like normal identifiers (that is. nil. This section begins by describing the syntax used to produce messages. . 2. false. but unlike identifiers they need not be declared.Syntax 15 At the command level. Of the seven. Class identifiers respond to a variety of messages that can be used to discover information concerning the class the object represents. The pseudo-variables selfprocess and smalltalk are unique to Little Smalltalk and are not part of the Smalltalk-80 system. . pseudo-variables. will be discussed in later sections. From now on the text will use the symbol oE. selfProcess2 . false. super. As we will see in a later chapter. Messages As noted in Chapter I. and smalltalk. is an instance of class Smalltalk and is used to centralize several pieces of information concerning the currently executing environment. We will discuss these three in more detail when we outline the behavior of different classes. 1.17 Instance variables defined at the command level are known only at the command level and cannot be used within a method for any class. Other types of objects in the Little Smalltalk system. The assignment arrow is formed as a twocharacter sequence consisting of a less than sign and a minus sign: 1 newname < . We will discuss these in more detail when we describe class methods and processes. all actions in Smalltalk are produced by sending messages to objects. False. The next three. See Appendix 5 for an overView of the differences between Little Smalltalk and the Smalltalk-80 programming environment. smalltalk. and selfProcess are farthest from being literal objects because their meaning depends entirely upon context. instance variables within a class must all be declared. new instance variables can be defined merely by assigning a value to a name. such as blocks and instances of user defined classes. For example. and UndefinedObject. There are several pseudo variables: self.

3 Binary messages tend to be used for arithmetic operations. although this is not enforced by the system and there are notable exceptions. depending upon the sign of the object the message was sent to (the receiver)."\ > \. Each keyword is followed by an argument. For example: 7 sign illustrates the message sign being passed to the number 7. such as an identifier or a literal. An example of a binary message is arithmetic addition: 7 + 4 At first the fact that this is interpreted as "send the message + with argument 4 to-the object 7"may seem strange. elicit a response. . or 1.' 16 The Language Any message can be divided into three parts. called a binary message. The response to sign is an integer. which is simply another object. or approximately 70. cannot be used to form binary messages.993. Unary messages have a higher precedence than binary messages. like unary messages. The receiver and argument portions of a message can be specified by other message expressions. parenthesis or periods. the first character must be lower case) followed by a colon. The argument can be any expression. a message selector. thus 7 + 17 sqrt evaluates as 7 + (l7sqrt). A keyword is simply an identifier (again. such as braces. not 19. Some characters. parse left to right. See the description in Appendix 2 for a more complete description of the restrictions. . '.0. or they may be specified by a single token. The selector for a keyword message consists of one or more keywords. not (7 + 17) sqrt. A unary message selector consists of an identifier. A binary message is formed from one or two adjacent nonalphabetic characters. either -1. like all messages. soon the uniform treatment of objects and message passing in Smalltalk makes this seem natural. Unary messages. although if the expression is formed using a keyword 3. takes one argument. the first letter of which must be lowercase. Binary messages. and zero or more arguments. The second form of message. The first type of message selector requires no arguments and is called a unary message. however. Thus 7 + 4 *3 results in 33. a receiver. Unary messages parse left to right. for example: 7 factorial sqrt returns V7f. so. The most general type of message is a keyword message.\ .

Keyword messages have lower precedence than either binary or unary messages. - Syntax 17 message. the expression will be evaluated and the result printed. produced at the left margin. Example keyword expressions are: 7 max: 14. no value was printed.." Try typing i <. Thus we say the message selector being expressed in the second example above is between:and:." Can you explain the outcome? Try "(5 + 4) sqrt.. Try typing "5 + 4 sqrt. After logging on. If. since assignment expressions do not have a value. There can be any number of keywords in a keyword message. However. Try typing 27 + 3 sqrt followed by last . Thus 7 between: 2 sqrt and: 4 + 2 is the same as 7 between: (2 sqrt) and: (4 + 2) Getting Started You now have enough information to try getting some hands-on experience using the Little Smalltalk system.. After a moment. Try typing "3 + 4" and see what happens. if you now type the most recent object assigned to the name will be produced. The name last always contains the value of the last expression computed. it must be placed in parentheses to avoid ambiguity. The result should be a 7. we catenate the keyword tokens.\~'-.. although in practice few messages have more than three. and the cursor should be indented by a small amount on the next line. 7 between: 2 and: 24 When we wish to express the name of the message being requested by a keyword message. the message "Little Smalltalk" should appear. The cursor then should advance to the next line and once more tab over several spaces.3 Notice that. at this point. you type in a Smalltalk expression and hit the return key. type the command st.

The message class. will tell you the class of an object. which is recognized by the boolean values true and false: (i isMemberOf: Integer)l(i isMemberOf: Float) . . For example. the last expression given is equivalent to i isMemberOf: Integer Suppose we want to tell if an object is a Number. for example. . Try typing 7 class The message superClass. Try typing Integer superClass 7 class superClass What is the superclass of Object? The keyword message respondsTo: can be used to discover if an object will respond to a particular message. will return the immediate superclass of that class. Try typing i i ~ == 17 17 17~~17 One way to tell if an object is an instance of a particular class is to connect the unary message class and the binary message = =. . but we don't care if it is any particular kind of number (Integer or Float). Integer respondsTo: #+ You can discover if two objects are the same using the binary message = =. the message respondTo: inquires whether instances of the class respond to the given message.' \. We could use the boolean OR bar (I). The argument must be a symbol.'\ . Try typing i class == Integer A simple abbreviation for this is the message isMemberOf:. The message~~is the logical inverse of = =. when passed to an instance of Class. representing the message. For example. Try typing 7respondsTo: #+ $A respondsTo: #between:and: $A respondsTo: #sqrt When passed to a ciass.\ ':- '> 18 The Language Finding Out About Objects There are various messages that can be used to discover facts about an object.\.

Like procedures. regardless of whether this is the current context or not.\ . Syntax 19 A simplier method is to use the message isKindOf. Since a block is an object. the evaluating message would be value:value:.\. i print] Within a block (and.. when the block given above is evaluated. it can be assigned to an identifier or passed as an argument with a message or used in any other manner in which objects may be used. In response to the unary message value. [:x :y I (x + y) print] is known as a two-parameter block (sometimes two-argument block). the i in the block will refer to the i in the context in which the block was defined. So. perhaps even in a different context. The message value: is used to evaluate a block with parameters the number of value: keywords given matches the number of arguments in the block. in general within a method) a period is used as a statement separator. is the same as the argument. and the value resulting from that block will be the value of the expression. For example. This feature is called a block (an instance of class Block) and is formed by surrounding a sequence of Smalltalk statements with square braces. \.\. The value returned by a block is the value of the last expression inside that block. One way to think about blocks is as a type of in-line procedure declaration. Try typing i isKindOf: Number - - Blocks An interesting feature of Smalltalk is the ability to encapsulate a sequence of actions and then to perform those actions at a later time. Even if the block is passed as an argument into a class in which there is a different instance variable i and then evaluated. a block will execute in the context in which it was defined. That is. for the example given above. as we will see in the next chapter. or any of superclasses. a block can also take a number of arguments. . This message asks whether the class of the object. Frequently a block will contain a single expression. Parameters are denoted by colon-variables at the beginning of the block. the identifier i will refer to the binding of the identifier i that was known at the time the block was defined. Thus a block when used as a parameter is similar to the Algol-60 call-byname notion of a thunk. followed by a vertical bar and then the statements composing the block. as in: [i~i + 1.

. 20 The Language Comments and Continuations A pair of double quote marks (II) are used to enclose a comment. What values will be printed in place of the question marks in the following sequence: i <. Should it be necessary to continue a long expression on two or more lines.23 i print Program Continued .\ . The Little Smalltalk system assumes that each line typed at the terminal is a complete SmaIItalk expression.17 j<-[i< j +1] print ?? j print ?? i < . for example: 2+ 3*7 +5 40 """"- EXERCISES 1. Type the following expressions: 7 = = 7 label label #abe = = #abc == How do you explain this behavior? 3. \ ~\. One must be careful not to confuse the double quote mark with two adjacent single quote marks CI). Show the order of evaluation for the subexpressions in the following expression: 7/2 between: 7 + 17 sqrt and: 3 *5 2. The text of the comment can be arbitrary and is ignored by the Little SmaIItalk system.\. a special ineJication must be given to the Little SmaIItaik system to prevent it from misinterpreting the partial expression on the first line and generating an unintentional error message. which look very similar. This special indication is a backwards slash C""-) as the last character on all intermediate lines.

\. Syntax 21 ?? j value print ?? i print ?? j value print ?? i print ?? - .

"...~.J}}}}}}}}}}}}}}}}}}}}}}}} (C««({««(((({{(...~ ~ '-. -~ :. * <::~ * * * * * * * * * * * * * * * * * * * :<.. .. . * * * * * <-: * .....~""~ ~ '". . :s::: :8\ ~: :~: ///////. CHAPTER '" '" '" '" /\ !\ -: .~ ~ i} * i} ~ <'~ <:. j'.\ '#: /\ .//// "'./\ /\.~\ (8': (2~ >~~ !\ !\ /\ ~~) !\ /. ~{{ {{{{{{{{ {{{{ {{{{{ {{ {{{{ f}}}}}}}}}}}}}}}}}}} n}}}} :«««{({«{«««««i ))))))))))))))\\\\\\\\\ 22 ..~ . // / ///// /// "'-" --". j\ . <:."-" "" ~ "{{{{i{{{{{{{{{{{{{{{{{{{{. j\ '#: "'it':: ~ ~ >i- !\ !\ ... (~ ~.. .. . 3 Basic Classes !\ !\ !\ /\ /\ 1\ . /'.

One important property of this class is that its instances are the only objects in the Little Smalltalk system that can be displayed on an . the class Number provides a convenient method for constructing points. which returns the object representing the class of the receiver.. Char. The class Point is used to represent ordered pairs of quantities. Magnitude. Radian. SequenceableCollection. For example. File. Only radians will respond to trigonometric functions such as sin and cos. Radians are normalized by adding or subtracting multiples of 2'11" from their value. Block). The following sections will briefly describe each of these categories. Thus 10 @ 12 generates a point representing the ordered pair (l0.12). False. The second value is the y-value and is returned in response to the message y. independent of other numbers. True.. Random. such as storing coordinate pairs in graphics applications. ~. A radian is a unit of measurement. List. scriptions for each of the standard classes. \. These groups are Basic Objects (Object. and System Management (Object. Symbol. the message = = is defined in class Object and is thus accepted by all objects in the Little Smalltalk system. Bag. Integer. Control Structures (Boolean. Dictionary. Appendix 3 provides detailed de. Another message defined in class Object is the message class. other types of objects are also basic to many applications. such as scaling by a numeric quantity or taking the difference of two radians. Array. ByteArray). This message tests to see if the expressions for the receiver and the argument represent the same object. Boolean. Ordered pairs are useful in the solution of many problems. False. radians can be converted into numbers by sending them the message asFloat. UndefinedObject. True. Float. Process). Only a limited range of arithmetic operations on Radians. instances of the class Radian are used to represent radians. Similarly. The last chapter introduced the classes associated with literal objects. Basic Objects The class Object is a superclass of all classes in the system and is used to provide a consistent basic functionality and default behavior. Number. . KeyedCollection. ArrayedCollection. All instances of class Number will respond to the message @ by producing a point consisting of the receiver and the argument. Collections (Collection. String. For example. Numbers can be converted into radians by passing them the message radians. Point). Class. In fact. Interval. Interval. are permitted.\. Smalltalk. Basic Classes 23 The classes included in the Little Smalltalk system can be roughly divided into four overlapping groups. Set. The first value is known as the x-value and will be returned in response to the message x. The class String provides messages useful in manipulating arrays of characters.

select:. the presence or absence of an ordering. values can be added to the beginning or end of the collection by using the messages addFirst: and addLast:. Here. Nevertheless. thus.24 The Language output device such as a terminal or printer. The behavior defined in class Object for the message print is to convert the object into a string (using the message printString) and then to print that string (by passing the message print to it). such as their access method (at: and at:put:) and their ability to respond to collect:. has no fixed size or ordering and can be indexed by arbitrary elements. By default (that is. A special case of this is the class List. on the other hand. Those collections that are indexed (Dictionary. Here collections divide into two groups. which maintains elements in a linear ordering. The different forms of collections are distinguished by several characteristics. an array is a collection with a fixed size and ordering. The message printString is uniformly interpreted throughout the Little Smalltalk system as "produce a string representation ofyour value. indexed by integer keys. Array) must be given an explicit key and value. We can group the operations into several categories. Those collections that are not indexed store only the value and thus use the one argument message add:. by a method in class Object that will be invoked unless overridden). asBag (to convert a collection into a Bag). and their insertion or access method. Any object to be displayed must first be converted into an instance of class String. a string containing the name of the class of the object is produced. and many other messages. the insertion method is the two-argument message at. for example. Collections The different subclasses and varieties of Collection provide the means for managing and manipulating groups of objects in Smalltalk. In collections that do not require . For example. asSet or asArray. The table below lists some of the characteristics of several forms of collections.·put:. whether the size of the collection is &ed or unbounded. The first basic action is adding an element to a collection. A dictionary. arrays and dictionaries share many features. Collections of one type can frequently be converted into collections of different type by sending an appropriate message." Classes for which this makes sense (such as Integer) must define a method for this message that will produce the appropriate string. In subsequent chapters we will see several examples of how different classes respond to the message printString. independent of the type of collection involved. and. Protocols for adding an element to a collection are similar to those for removing an element from a collection.

3 4 5) 4 An error message is produced and nil returned if no value satisfies the condition.2 . A special case is List where one can access either the beginning or the end of the list by using the messages first and last. For example. an element can be removed with the message remove:. In a List. x x detect: [ :y I y > 0 ] ~ # ( . the removal message uses the key (removeKey:). For those that do not require a key. .. The access methods at: and includes: access a value by position.. an element can be removed from either the beginning of the list (removeFirst) or the end of the list (removeLast). s \ . one needs to access an element by value without knowing a position. if x is an array containing numeric values. the next step is to access the element. however. the message detect: could be used to discover the first positive value. one may want to find the first positive element in an array of integers.Basic Classes 25 Name Creation Method new new nto: m new new: new: Size Fixed? no no yes no yes yes Ordered? Insertion Method add: at:put: none addFirst: addLast: at:put: at:put: Access Method includes: at: at: first last at: at: Removal Method remove: removeKey: none removeFirst remove Last none none Bag/Set Dictionary Interval List Array String no no yes yes yes yes a key. In collections with fixed sizes (Array and String). The message detect: takes as an argument a one-parameter block. This can be changed using the message detect:ifAbsent: . and not the value. In keyed collections. Once an element has been placed into a collection. To facilitate this search there is a message named detect:. It evaluates the block on each element in the collection and returns the value of the first element for which the block evaluates true. elements cannot be removed. the only question (since one already has the value)-is whether the value is in the collection. Thus the appropriate message is includes: (which also works for keyed collections). Frequently. the argument being the object to be removed. Those collections using keys require a key for access and use the message at:. \ '. For example.

the message collect: can be used. The action performed in response to this message is to loop over each element in the . the message used to accomplish this is inject:into: The message inject:into: takes two arguments: a value and a two-parameter block. The elements of the new collection. reject:. If. A similar message. If. you want to produce a new collection containing the results. this message takes as argument a one-parameter block and returns a collection of the same variety as the receiver. are the results of the argument block on each element of the receiver collection. containing those values for which the argument block evaluated true. Like select: and reject:. whereas in unordered collections. of the same type as the receiver.\ \ i- \ :~ \ 26 The Language x detect: [ :y I y > 10 ] error: no element satisfies condition nil 23 x detect: [ :y I y > 10] ifAbsent: [ 23 ] In ordered collections. instead of finding the first element that satisfies some condition. this message takes a oneargument block. you want to find all elements of a collection that satisfy some condition. the search is performed in order. The action performed is to evaluate the block on each element of the collection. however. An example would be taking the sum of the elements in a numerical array. What it returns is another collection. select: takes as an argument a one-parameter block. Like detect:. In Little Smalltalk. and no specific order is guaranteed. x select: [ : y I y > 0 ] #( 45) x reject: [ : y I y > 0 ] #( -2 -3) The message do: can be used to perform some computation on every element in a collection. Again like select: and reject:. then the appropriate message is select:. returns the complementary set. x collect: [ :y I y sign] #( -1 -1 1 1 ) Frequently the solution to a problem will involve processing all the values of a collection and returning a single result. the search is implementation dependent. instead of performing a computation on each element. x do: [ :y I (y -1 + 1 ) print] -2 5 6 The message do: returns nil as its result.

although commonly the keys are instances of String. they are described in detail in Appendix 3. There are many other messages specific to certain classes. Thus a list can be used to implement both a stack and queue. A file can be opened . be accessed randomly using the message at:. A Dictionary is also an unordered collection of elements. They can. Both the key and value portions of a dictionary entry can be any object. For example. Insertion and removal is from either the beginning or the end of the collection. either ascending or descending. (2 to: 7 by: 3) at: 2 5 A List is a group of objects having a specific linear ordering. Elements are added and removed by value. A File is a type of collection in which the elements of the collection are stored on an external medium. however. The class Interval represents a sequence of numbers in an arithmetic progression. In conjunction with the message do:. an Interval creates a control structure similar to do or for loops in Algol-like languages. Instances of Interval are created by numbers in response to the message to: or to:by:. The classes Bag and Set represent unordered groups of elements. We next will provide a brief overview of the most common types of collections. however.Basic Classes 27 collection. insertions and removal of elements from a dictionary requires an explicit key. Symbol or Number. (1 to: 10 by: 2) do: [ :x I x print] 1 3 5 7 9 Although instances of class Interval can be considered to be a collection. passing the element and either the initial value or the result of the last iteration as arguments to the block. the sum of the array x could be produced using inject: x inject: 0 into: [ :a :b I a + b] 4 The following command returns the number of times the value 4 occurs in x: x inject: 0 into: [ :a :b I (a 1 = = 4) ifTrue: [b + 1 ] ifFalse: [ b ]] We have described the broad categories of messages used by collections. they cannot have additional elements added to them. An element may appear any number of times in a bag but only once in a set. typically a disk. unlike a bag.

for example: #[ 0 127 32 115 ] There are two other classes that are commonly used to representgroups of data. many of the operations on files may be quite slow. The class Random can be thought of as providing protocol for an infinite collection of pseudo-random numbers. for example: #( 2 $a 'joel 3. This "list. In Integer mode every read returns a single word as an integer value.1415 ) A String can be considered to be a special form of array. Byte arrays can be written as a pound sign preceding a list of elements enclosed in square braces. while elements cannot be inserted or removed from an array. is the conditional test: IF some condition is satisfied THEN perform some actions ELSE perform some other actions. In character mode every access or read returns a single character from the file. The values produced by instances of class Random are floating values in the range 0. In string mode every read returns a single line as an instance of class String. and. Arrays have fixed sizes.28 The Language in one of three modes. The class Point. Elements cannot be removed from a file. Because access to external devices is typically slower than access to memory. as we have been illustrating in many examples. An Array is perhaps the most commonly used data structure in Little Smalltalk programs. In addition. Literal arrays can be represented by a pound sign preceding a list of array elements. Other messages can be used to convert this into either an integer or a floating value in any range.0. already discussed. The class ByteArray represents a special form of array where each element must be a number in the range 0 through 255. although they may be overwritten. Control Structures One of the more surprising aspects of Smalltalk is the fact that control structures are not provided as part of the basic syntax but rather are defined using the message passing paradigm. The basic control structure in Smalltalk. can be considered to be a small collection of two items. where the elements must be characters. the elements can be overwritten. a literal string can be written by surrounding the text with quote marks. as in most computer languages. Byte arrays are used extensively in the internal representations of objects in the Little Smalltalk system. is never actually created in its entirety.0 to 1." of course. rather each number is generated as required in response to the message next. In Smalltalk this is accomplished by passing messages to instances . although they are not subclasses of Collection.

the argument block used with the and: message is not evaluated. For example. the value nil is returned. a superclass of both True and False. Basic Classes 29 of class Boolean.. 5 timesRepeat:{ 8 print] 8 8 1. A loop is produced by passing the timesRepeat: message with a parameterless bock as an argument to an integer. the receiver is true and the message is ifTrue:.' Notice that the relational < returns either true (an instance of class True) or false (an instance of class False) and that the message and: is implemented in class Boolean. the expression "(b at:i) = 4" will be evaluated only if the expression <10<10)" is true.. and the result it produces is returned. the argument block is evaluated only if necessary to determine the result of the boolean expression. that is.of class Block. the argument block is evaluated. If the condition is satisfied (Le. or the receiver is false and the message is ifFalse:). In actual fact the Little Smalltalk parser will. And: i and or: provide "short circuit" evaluation of booleans. If the condition is not satisfied. the most common control structure is a loop. The class True (a subclass of Boolean) defines methods for the messages ifTrue: and ifFalse: (similar methods are defined for class False). for efficiency. (3 < 5) ifTrue: { 17 ] 17 (3 < 5) ifFalse: { 17 ] nil The combined forms ifTrue:ifFalse: and ifFalse:ifTrue: are also recognized: (3 < 5) ifTrue: { 17 ] ifFalse: { 23 ] 17 (3 > 5) ifTrue: { 17 ] ifFalse: { 23 ] 23 The message and: and or: are similar to ifTrue: and ifFalse:. ((i < 10) and: { (b at: i) = 4] ifTrue: { i print] In this example. the underlying paradigm holds true and will. Various other boolean operations. such as not are also defined in this class. The arguments used with these messages are blocks. They are also used with booleans and passed as arguments objects. If the first expression returns false. when the arguments are not a block). in fact. The value of the integer is the number of times to execute the loop. Nevertheless. be used under some conditions (for example.\ ~\. Next to conditional tests. often optimize conditions to remove the message passing overhead. .

by using an additional variable the previous loop could have been written: i~2. [i print. The actions performed by the block are to evaluate the receiver block and. Classes themselves cannot be created by new . The result of the receiver block must be a boolean. produce an instance of class Interval. \ ~\ ::". For example. for example. For example: i~2. i 2 5 8 ~ i + 3] Since both the receiver and the argument block can contain any number of expressions (the value of a block is always the value of the last expression. to evaluate the argument block. [ i < = 9 ] whileTrue: [ i print. As we noted in the last section." 30 The Language 8 8 8 will print the number 8 five times. Thus. an interval is a collection of values in arithmetic progression. The messages to: and to:by:. We can then use the message do: to enumerate the elements in the progression. as long as it returns true. The unary message whileTrue (or whileFalse) can then be used. A while loop is formed using blocks as both receiver and argument. sometimes no argument block is necessary. regardless of how many expressions the block contains). \ :. the methods new and new: are implemented in class Class to allow instance creation. For example: (2 to: 9 by: 3) do: [ :x I x print] 2 5 8 A more general form of loop is the while loop.~.\ . 8 Class Management The class Class is used to provide protocol for manipulating classes. A more general loop is used to produce numbers in arithmetic progression. i 2 5 ~ i + 3 . i < = 9] whileTrue. when passed to a number.

later definitions of new are in addition to. smalltalk time:[ ( 1 to: 10000 ) do: [:x 104 I] ] Smalltalk is a subclass of Dictionary and thus responds to the messages at: and at:put:. The integer value it returns represents the number of seconds elapsed in evaluating the block. By passing messages to smalltalk. The necessity for global variables is often the mark of a poorly developed solution to a problem. definitions higher in the class hierarchy). and do not override. This happens at all levels of the class hierarchy. it is easy to overlook. the newly created object is immediately initialized by sending it the same message. (That is. Since smalltalk is accessible anywhere in any object. Although permitted. In later chapters we will see how this feature can be used to automatically initialize objects. One should be careful to distinguish the message new passed to the class object used to create the object from the same message passed to the newly created object used for initialization. Since the second message is produced internally. responsibility to insure that two objects do not try to store different information using the same key. Of course. With the exception of message passing. it is the usees . The messages new and new: are treated differently in one respect by the Little Smalltalk system: if a class defines a method for these messages. requires a block as argument. the use of the pseudo-variable in this manner is at odds with the pure object-oriented philosophy of Little Smalltalk and should be discouraged. each time a new instance of the class is created (by sending the message new or new: to the class object). and not by the user. the user ~an discover and set many attributes of the currently executing environment.Basic Classes 31 but must be generated by compilation (which is the topic of the next chapter). sma IitaIk date Fri May 24 14:03: 16 1985 Another message. then. The class Smalltalk provides protocol for the pseudo variable smalltalk. Array new: 3 #( nil nil nil ) UnoefinedObject new nil The argument used with new: is not used by the object creation protocol but only by the object initialization method. . and the resulting object is returned to the user. it can be used to pass information from one object to another or to provide global information used by a number of objects. this pseudo variable is the only means of communication between separate objects. time:. even if the message is defined multiple times.

are what are known as abstract superclasses. SequenceableCollection.03. whereas any common behavior.\ . is very useful. or Number. KeyedCollection. ~:} > 32 The Language The pseudo variable smalltalk also provides a means to construct and evaluate messages at run time by using the message perfonn:with Arguments:. with the remainder of the arguments in the second array as the argument values for the message. the object is not particularly useful. An instance of class Set. if one were designing a system to manipulate banking accounts. The second argument must be an array representing the receiver and arguments to be processed. such as the actions necessary for opening and closing an account or querying the balance. For example. It has no insertion or deletion protocol.. . For example. along with such classes as Boolean. The selection and design of abstract superclasses is one of the more important arts in Smalltalk programming.14) True An instance of class Process is used to represent a sequence of Smalltalk statements in execution. for example. \ ~~ ~. The response is the value returned by the first argument of this array in response to the message. The second argument must be an array representing the receiver and arguments to be used in evaluating the message. The actions specific to the individual types of accounts would be in the subclasses. Magnitude. Processes cannot be created directly by the user but are created by the system or by passing the message newProcess or fork to a block. a class Account might be a useful abstract superclass for classes CheckingAccount and SavingsAccount. but the classes are important in providing methods that can be inherited by subclasses. or ArrayedCollection in the last section. The first argument to this message must be a symbol indicating the message to be processed.. For example: smalltalk perform: #between:and: withArguments: #(3 1. Abstract· Superclasses We did not discuss the classes Collection. Instances of abstract superclasses are seldom useful by themselves. might be implemented in the superclass. even though they were listed as forms of "collection" in the beginning of this chapter. however. Processes will be discussed in more detail in Chapter 10. These classes. and the messages defined in class Collection are important in providing functionality to objects of this class and to other subclasses of class Collection. while it is legal in Smalltalk to say: x ~ Collection new and the resulting variable x will indeed be an instance of class Collection.

Discuss the advantages and disadvantages of these two different arrangements. . An alternative would have been to eliminate the class Radian and to permit all objects of class Float to respond to the messages sin and cos. a) implement reject: in terms of select: b) implement size in terms of inject:into: c) implement includes: in terms of inject:into:. How would you go about producing an instance of the class List containing the numbers listed in sorted order? What is the class of Class? what is the superclasss of Class? Many times. In each of the following. describe a sequence of messages that will have the same effect . Note that the messages arcSin and arcCos produce an object of type Radian and not of type Float. Suppose you have a Bag containing numbers. as follows: I~UndefinedObject new 2. How does i respond to print? To isNil or notNil? To the object comparison message = = with nil ? Is i nil? List facts to support your answer. two or more different sequences of messages to collections will have the same effect. 4.Basic Classes 33 - EXERCISES 1. 3. 5.\S the indicated message. Suppose you created a new instance of the class UndefinedObject. Furthermore. only objects of type Radian respond to sin and cos.

}}}H}}}}}}}}}}}}}}}}}}} r ««{{{««««{«««(. 4 Class Definition . \- CHAPTER / / / /./// / / / .{{{{{{{{{{{{{{{{{{{{H{{{ "" '" '""'" '" '" '" "" '---- }}}}}}}}}}}}}}}}}}}}}}}}}} {«{«({{«««««««i \\\)))))))))\\\\\\))))) 34 ./ "'" '" "'" "'" '" "'" "". '" ""'-" {{{{{{{{H{{{{{{{{{{{{{W ./ / / / // / //// .\."\ .

\ . if not given. Each method in the class protocol defines how instances of the class Figure 4. in this case the description for the class Set.1 0 Class description for class Set I list I [ Class Set: Collection first message pattern first message method second message pattern second message method . This chapter will show how the user can define new classes to provide additional functionality for the Smalltalk language. Each class instance is given its own set of instance variables.1 shows a prototypical class description. the class description lists instance variable names. class descriptions must be prepared beforehand in a file. The syntax for class descriptions is given in detail in Appendix 2. followed by a sequence of one or more m. Note that Smalltalk has no notion of variables being declared to be of a specific type. After the class and superclass names. Following the heading. Figure 4. is given. a pair of square braces surround the methods that comprise the protocol for the class. "\ . Following the class name. it is conventional to place the instance variables on a line separate from the class name. can be omitted. Instead. The superclass name is optional and. the class Object will be assumed. We will discuss each portion in turn. consists of the keyword Class (the first letter capitalized) followed by a class name (also capitalized).'. including the vertical bars. the superclass name preceded by a colon. The initial part of the description. Class descriptions cannot be entered directly at the keyboard during a Little Smalltalk session. Instance variables are initially given the value nil. If a class does not contain any instance variables. There can be any number of class descriptions in a file. although it is convenient to keep each class description in a separate file for ease in program development. thus any instance variable can contain any accessible object or expression. The textual representation of a class description consists of two main parts.ethod descriptions. .\ Class Definition 35 The last chapter introduced some of the standard classes in the Little Smalltalk system.' \. using a standard editor. the class header. Instaqce variables provide the local memory for instances of the class. Although the syntax is free form. the entire list. the class Heading. The list of instance variable names is surrounded by a pair of vertical bars. and no other object can modify or change an instance variable in another object.

Note that the period is an expression separator. the other two portions are a list of temporary variables and the message body. [ item notNil ] whileTrue: [ aBlock value: item.) If no explicit value is returned. The particular message being defined by the method is given by the message pattern. to a single message. and they exist as long as the message body is being executed or there is a block in existence that can refer to the temporary value. The pattern is the first of the three major portions ofa method. Temporary identifiers are created when the method is initiated in response to a message. t oldObject oE- self next ]. (On some terminals the up arrow looks like a <lcaret" or". A message pattern defines both the name of the message for which the method is providing protocol and the names to be used when referring to parameters associated with the message. There are three forms of message pattern. item t nil isEmpty t (self size = 0) remove: oldObject self remove: oldObject ifAbsent: [t self error: 'attempt to remove object not found in collection' ]. For example.self first. Figure 4. not an expression terminator. temporary variables are initially given the value nil. and does not follow the final expression in the method body. A method body consists of a sequence of one or more Smalltalk expressions separated by periods. The final expression in a method body.2. can be preceded by an up arrow to indicate that the following expression is to be returned as the response to the message. and keyword). An optional list of temporary identifier names can appear following a message pattern. the default action at the end of a method is to return the receiver as the response to the message. the message description for the message do: in Figure 4. Example methods using two of those forms are given in Figure 4. or the final expression in any block created within the method body. The one exception to the free form notation for class descriptions is that this vertical bar must appear in the first column. Like instance variables. corresponding to the three forms of messages (unary. binary. These methods are from class Collection. Note that method descriptions are separated by vertical bars.2 lists a temporary identifier named item. .\ 36 The Language will respond.2 0 Method descriptions from class Collection do: aBlock I item I item oE.

namely instance variables. If. .3 gives the complete class description of the class Set. when a message is passed to super. Thus the search for a nlethod matching the message size would begin in class Set. on the other hand. suppose variable "x" is an instance of class Set. This is different from a block returning a value. (See Exercise 2. nil and smalltalk). and illustrates the use of this feature. A set is a collection of objects. if an expression in class Set (Figure 4. To illustrate the actions of self. the search for a corresponding method would have begun in the class Object. when an instance of that class is created. The methods shown in Figure 4. a superclass of class Set. the superclass of class Set. the associated message (either new or new:) will automatically be passed to the new instance before any further processing. temporary variables. the class Set. in turn. the search for a method begins with the class description associated with the class of the receiver. " the method isEmpty shown in Figure 4. the super class of Collection.2 is initiated. passes the message size to the pseudo variable self. super. Thus. A message passed to self would initiate a search for an associated method in class Set. the class of x.1) passed a message to super. Recall that a list is also a collection but one that maintains values in order. Both the variables self and super refer to the receiver of the current message. If either of the messages new or new: is defined in a class protocol. Two messages are singled out for special treatment. the pseudo variables self. When a message is passed to self. as we saw in Chapter 2. and selfProcess can be used. these messages can be used to provide automatic initialization of instance variables. each object occurs no more than once in the collection. the search for a matching method would begin in class Collection. In response to the message "x isEnzpty.2 are from class Collection. argument variables. Within a method body there are four types of variables that can be accessed. Class Definition 37 Note that the up arrow indicates an immediate return from the current method description even if the arrow occurs within a block. is implicitly the value of the last expression in the block. and pseudo variables. false. An Illustrative Example Figure 4. This method. which in this case represents x. The next section describes in detail one class.) The data structure used to implement the set will be a list. the method for isEmpty had passed the message size to the pseudo variable super. In addition to the pseudo variables discussed in the last chapter (true.\. On the other hand. the search begins with the class description of the superclass of the class in which the current message is defined. For example. which. then.

the class Collection defines the short message (re1110ve:) in terms of this longer message. When an element is added to a set. If so. Note that the long form of the remove: message is defined. list. if not. to remove an element.38 The Language Figure 4. Similarly. it includes a second argument. Each instance of class Set will have a separate instance variable that cannot be accessed by any other object.2. Generators will be the topic of a later chapter. the actions in the method in Set merely pass the removal message on to the underlying list. the class Set maintains one instance variable. The mes- . the element is added to the underlying Jist. it is sufficient to say here that first and next provide a means to enumerate all elements in a collection. Thus either form ofthe message can be used on a Set. As we saw in Figure 4. a test is first performed to see if the element is already in the set.3 D Class description for Set Class Set :Collection I list I [ new list~List new add: newElement (list includes: newElement) ifFalse: [list add: newElementJ remove: oldElement ifAbsent: exception Block list remove: old Element ifAbsent: exceptionBlock size t list size occurrencesOf: anElement t (list includes: anElement) ifTrue: [ 1 J ifFalse: [ 0 J first t list first i list next next As we have already seen. no further processing is done. The messages first and next are used to produce generators. The method for message new will automatically create an instance of class List in the variable list whenever an instance of class Set is created. indicating what actions should be taken if the given item is not found.

Whether there are syntax errors or not. i~Te5t new There are other system commands.' When the user exits the editor. the defined class can be used the same as any other class in the Little SmalltaIk system. the next step is to add the class to the collection of standard classes provided by the Little Smalltalk system. In Class Set. a new instance can be created using the command new. for example. If there are syntax errors. the generation of items in response to these messages is provided by passing the same messages to the underlying list. the class definition will be added to the collection of classes known to the Little Smalltalk system." Similarly. if there are syntax errors.\: Class Definition 39 sage first can be interpreted informally as flinitialize the enumeration of items in the collection and return the first item. \ ~\ \ .st The )e command will place the user in an editor mode to view the designated file. to add this class definition to a running Little Smalltalk execution you would type the following command in place of a Smalltalk expression: )i test. . the class description can be edited without leaving the Little Smalltalk system. you have defined a class Test in the file test. or nil if there are no items in the collection. similar to )i and k. type the following command: )e test. or nil if there are no remaining items.st The i can be thought of as mnemonic for "include. and. a number of descriptive error messages will be produced. Once a class definition has been successfully included.2) illustrates how these messages can be used to construct a loop to access every item in a collection. On UNIX Systems the editor selected can be changed by modifying the EDITOR environment variable. another attempt will be made to include the file. For example.1· ~~ \ . If there are no syntax errors." The protocol for do: (Figure 4. Processing a Class Definition Once you have created a class description. next can be defined as "return the next item in the collection. described in 1. Suppose. To do this." At this point the class description will be parsed. the cursor should advance by one tab stop when the system is ready to accept the next command. as if the )i command were typed.st.

1. One way to implement the class Bag would be to use a dictionary. The collections described in Chapter 3 were all linear. meaning they could all be represented by a linearly written list. Change the name of the class from Bag to MyBag. rather than as messages passed to smalltalk. The bootstrapping of the Little Smalltalk system will be discussed in the second section of this book. I diet count I [ new Class Bag :Colleetion dict~Dictionary new several missing methods first (count ~ diet first) isNil ifTrue: [ count ~ count . similar to the way a List was used to implement the class Set in Figure 4.40 The Language Appendix 1. A Bag is similar to a Set. The framework for such an implementation is shown below. t diet currentKey] ifFa Ise: [eou nt~ iet next]]. and complete the implementation of class Bag. the current key is accessible if you use the message currentKey. because they are used during the bootstrapping process before any objects have been defined. These commands are provided with an alternative syntax. - - EXERCISES 1. each entry may occur any number of times. t nil]. t diet currentKey next [count notNil]whileTrue: [(eount>O) ifTrue:[eount~eount-1. however. However. Implement the .3. 2. The value contained in the dictionary for a given entry would represent the number of times the entry occurs in the bag. t nil Keep in mind that instances of Dictionary respond to first and next with values and not keys. A common nonlinear data structure in computer science is the binary tree.

How should instances of BinaryTree respond to the enumeration messages first and next? How about print or printString? What should be . Instances of the class should respond to the following messages: left left: right right: value value: Return the left subtree. Return the value of the current node. Instances of BinaryTree contain a left subtree (or nil). Return the right subtree. and a value. a right subtree (or nil). Set the value of the current node to the argument.Class Definition 41 class BinaryTree. usually either nil or other instances of BinaryTree. Set the right subtree to the argument value.the superclass of BinaryTree? . Set the left subtree to the argument value.

\ ~\'. ' CHAPTER .:: ..'-" .- ($) * * :it: * (~ % * * ~B> % * x(§:: '%: * ~ ~p~ * * . "" """"'..i\ ~ <. <: .~: * ~ ~ (2E * * (8) r--.::. {{H{{{{{{H{{{{{{{{ {{{{{ 1 ..:. / / / / / / ///'/ / "-....~_.S0 -. < ~ :< * * * * * * * * * * * * * * * * * * >~ ~.~ :"""'\. - 1\ /\ /\ f\ f\ A f\ . /\ .: <::. " .}}}}}}}}}}}}}}}}}}}}}}}}} «««««««««««(.~ * % * <: <::: <. 1.. 5 A Simple Application i\ f . 'I' "{'{{I' '{{{{f 'J{ tf{{1 (" ~)H{ )U _"dl )Ht f}}}}}}}}}}}}}}}}}}}}}}}}} ((({(((((((((1 \\)))l))))))))))))))))) 42 . % * * * :::j. ~< <::" f\ 1..«::.// """" /\ !\ /\ f\ f\ ". f\ .// /// / / / /_. ....<: < <. ~p\ ~ ~ . '%: ~ * <". f\ f\ /\ 1\ * * * /\ (~-~ ~ * * ~ /\ ..: . " """" "" "" """"" ".

Using this technique. The application chosen. However. Because programming in Smalitalk is so easy compared to many other programming languages. it is likely that there will be many changes in the design stage of any realistic piece of softwar:e. fix it by "patching. A Simple Application 43 This chapter depicts the development of a simple Smalltalk application. ignoring for the moment such features as user friendliness and efficiency. and to rewrite it than to take a poorly designed and less than adequate program and try to . High level logical mistakes are most easily exposed with the aid of an executing system. and start anew. you experiment with it. You then design the simplest. It is far better to throwaway the first attempt at a program. a tool to help keep track of employee information in a small business. but also describing the intended user interface (the set of messages to which the application should respond). is considerably less important than the design techniques being illustrated. you strive to find the core of the functions you want at the expense of enhancements or features you might think ultimately desirable. Thus time spent creating a complete system. you first define a minimal system that will still exhibit the important aspects of the desired functionality. and oftentimes more. a notion nearly impossible to define and almost as difficult to predict before you have a working system. It js also likely (perhaps unfortunately) that the user's concept of the problem at hand and the correct solution also will change as the user's experience with the initial versions increases. is deciding exactly what you want to do. This involves not only stating the desired functionality.\. and thus a working version helps considerably in getting more complex versions mnning. (Even with the best planning it may be necessary to throwaway at least one version. Once you have an executable system. The first. after learning from the mistakes. we must be able to add and delete information from the records. even one that is very simple. The n~cessity for devoting time to a complete and thorough job of specification and design cannot be understated.") What is the minimal functionality we could desire for our employee database? At the simplest. beyond a minimal system for experimentation purposes may not be productive. There is a great psychological benefit to having an executing system. most straightforward implementation of this bare system. An important aspect of software is its feel. a particularly attractive technique of developing an application is rapid-prototyping. That is."\ . But what information? Let us start with four fields: name idNumber a string giving the name of the employee a unique internal identification number . This is not a sign of poor programming practice. Users often discover that the set of enhancements they thought were important before a project was implemented are not the same as those they want after experimenting with the initial version. and probably one of the most important steps.

Instances of EmployeeRecord will themselves contain instance variables for each field of interest.1~ \ \> \ " 44 The Language position salary a symbol representing the employee job classification a number giving the salary of the employee In a realistic situation there would probably be many more fields one would want to maintain (length of employment. we need not create any new classes at all. and names may not be) and the value field can be the rest of the information. For a first approximation. We can merely use an instance of class Dictionary as our database and an Array for each employee. One of the most serious deficiencies is that the user of the database must know the position of each field in the employee record in order to understand or update the information. and so on) but the four fields listed are sufficient for our examples. One mistake in updating (using the wrong field number. it is obvious that this scheme has some deficiences. department. EmployeeRecord. Therefore our first improvement will be to replace each employee record with an instanc~ of a new class. A pair of messages is defined . The key for each dictionary entry can be the employee number (since they must be distinct. employeeDatabase ~ Dictionary new employeeDatabase at: 14737 put: # ( 'David Smith ' # clerk 14000 ) employeeDatabase at: 16432 put: # ( 'Roger Jones l # clerk 13500) employeeDatabase at: 2431 put: #( 'Fred Brown ' # president 68020 ) Information on a particular employee can be extracted using at: employeeDatabase at: 16432 # ( lRoger Jones # clerk 13500 ) l Searches of various sorts can be performed by using select: employeeDatabase select: [:x I (x at: 2) = = # president] Dictionary ( 2431 @ #( 'Fred Brown ' president 68020 ) ) employeeDatabase select: [:x I (x at: 3) < 20000 ] Dictionary ( 14737 @ #( 'David Smith l #c1erk 14000 ) 16432 @ # ( 'Roger Jones l # clerk 13500 ) ) Output which looks slightly better can be obtained by using do: employeeDatabase do: [:x I «x at: 3) #( 'David Smith ' #c1erk 14000) #( 'Roger Jones l #c1erk 13500) < 20000) ifTrue: [ x print] ] A record can be updated by combinations of at: and at:put: (employeeDatabase at: 16432) at: 3 put: 13750 employeeDatabase at: 16432 # ( 'Roger Jones l # clerk 13750) While it required almost no work to produce this first approximation. for example) can damage the record badly. social security number.

the default method (in class Object) produces the class name. employeeDatabase ~ Dictionary new employeeDatabase at: 2431 put: EmployeeDatabase new (employeeDatabase at: 2431) name: IFred Brown ' (employeeDatabase at: 2431) position: #president (employeeDatabase at: 2431) salary: 68020 employeeDatabase at: 2431 EmployeeRecord (employeeDatabase at: 2431) position #president Let us deal first with the problem of the difficulty in retrieving all information at once. printString t Iname : II name. I position: I. the result is more helpful. not the entire structure. The following class description shows one of these pairs. Also it is now possible to retrieve only a single field. If no method is provided for this message. employeeDatabase at: 2431 name: Fred Brown position: president salary: 68020 The solution to the problem of initialization is slightly more complicated. however. Class EmployeeRecord I name idNumber position salary [ name: aString name ~ aString name I t name We still use a Dictionary for the entire database but replace the entries in the dictionary by instances of EmployeeRecord. The message printString is used uniformly throughout the Little Sn1alltalk system to produce a printable representation of an object. In a certain sense we have complicated matters since it is now necessary to initialize each field separately.\ \ ~} - A Simple Application 45 for each field: one to set the value and one to return it. we can create a new message initialize and use the . Isalary: I salary I Now when we try to print an individual record. of the employee record. as illustrated above. As a first step. position. by concatenating the various fields with strings showing the field names. We can produce a more meaningful output.

'salary: I printNoReturn salary <E-. you merely pass the message initialize to the record for that-individual and retype the information.\ -. then this message is passed to each new instance of that class as it is created. we can define the following message in class EmployeeRecord: new self initialize The message initialize will then be sent automatically to each new instance of EmployeeRecord. initialize I name : I printNoReturn. employeeDatabase <E-. We still can use the messages we defined at the start to update single fields individually.smalltalk getString aslnteger Thus we can initialize all the fields at the same time with one command: employeeDatabase <E-. we use the printing command printNoReturn. to update a record on a specific individual. before the new object is returned to the user. making changes where appropriate. If the class of an object defines a method for the message new..smalltalk getString.smalltalk getString asSymbol. Thus. 'position: I printNoReturn. Both of these alternatives may be error prone. name <E-. A better scheme would be to display each field as it . position <E-.Dictionary new employeeDatabase at: 2431 put: EmployeeRecord new (employeeDatabase at: 2431) initialize name: Fred Brown position: president salary: 68020 employeeDatabase at: 2431 name: Fred Brown position: president salary: 68020 There is a shortcut in Smalltalk to make the initialization of newly created objects easier. In order for the prompt and the response to appear on the same line. That is.Dictionary new employeeDatabase at: 2431 put: EmployeeRecord new name: Fred Brown position: president salary: 68020 employeeDatabase at: 2431 name: Fred Brown position: president salary: 68020 One can update a record in the same manner as initialization. \ \ " \ ~\ 46 The Language message getString with the pseudo variable smalltalk to return a string in response to a prompt.

including the current value. passing messages to self for each individual field. .\ A Simple Application 47 is currently contained in the database. the current value would· be retained. currentValue. Thus the abstract actions. Thus there are two essential pieces of information that must be passed as arguments. (Recall the messages required to list all employees with salaries less than 20. giving the current value of the field. like EmployeeDatabase. then making any changes desired. the string with which to prompt and the current value for the field. if blank. the system would prompt for each field.) A better scheme would be to create a new class. For that lower level message. it is necessary to print the prompt.'. updating. (reply size = 0) ifTrue: [ i currentValue] ifFalse: [ t reply] Now let us return once more to the representation of the database as a whole. As with initialize. Thus it is easier to abstract the desired behavior into a lower level message. reply ~ smalltalk getString. for example. and listing employee entries. namely. must be phrased (sometimes awkwardly) in terms of messages Dictionary understands. if not blank. (employeeDatabase at: 2431) upDate name (Fred Brown): position (president): salary (68020): 72000 employeeDatabase at: 2431 name: Fred Brown position: president salary: 72000 Here only the salary field was changed. or. return the user's response. retrieve the user's response. namely creating. and. 1( I.·put:. return the current value. We have up to this point merely been using an instance of class Dictionary and the insertion and deletion messages at: and at. I ):1) printNoReturn. If the user merely typed return. We can write upDate as follows: upDate name ~ self promptString: Inamel currentValue: name position ~ (self promptString: 'position' currentValue: position) asSymbol salary ~ (self promptString: 'salari currentValue: salary) aslnteger I promptString: aString currentValue: aValue I reply I ( promptString. Note that we are performing the same actions for each field. that would respond to messages more appropriate for our application. A set of messages might be the following: newEmployee .000.

upDate: aNumber update the fields for the employee with the given number. This can be established at the time an instance of the database class is created. A typical session using this class might appear as follows: employeeDatabase ~ EmployeeDatabase new em ployeeData base newE mployee id: 2431 name: Fred Brown position: president salary: 68020 employeeDatabase update: 2431 name (Fred Brown): position (president): salary (68020): 72000 employeeDatabase list: [:x I x position #president] name: Fred Brown position: president salary: 7200 • = The new database must still retain the information somewhere. list: aBlock print out information on all employees that satisfy the condition given by the argument block. . upDate: idNumber (records at: idNumber) upDate list: condition records do: [:x I (condition value: x) ifTrue: [ x print] ] The message new Employee requires an additional prompt to return the employee number. using the special semantics of the new message.48 The Language create a new employee record. I records I [ Class EmployeeDatabase new records ~ Dictionary new The messages upDate: and list: merely pass appropriate commands to the dictionary. One way is to maintain an internal dictionary.

A saved environment can later be restored by typing the command )1 filename This command loads the environment saved in the indicated file. newNumber ~ smalltalk getString aslnteger. you can then continue execution or exit the Little Smalltalk system by typing control-D. or experiment yourself. The command )s filename saves a representation of the current environmerh in the indicated file. rather it must be done periodically. Notice that in doing so. I In response to this command. records at: newNumber put: EmployeeRecord new I The exercises propose various further extensions that could be made to this application. Check with your system manager. to see if they work on your system. a good alternative is to use the Little Smalltalk system to save and restore environments. The )s and )1 commands do not work on all machines on which the other portions of the Little Smalltalk system will operate. One way to update an employee database is to record the final values for the database on a slip of paper. . Since computers usually have a much better memory for such things. a message indicating the number of bytes written to the file will be printed. These files tend to be rather large. All names that denoted accessible objects before the )s COffi- 1. After saving an environment. This may be somewhat unsatisfactory. Before the start of the next session you initialize the database by initializing each entry. it totally erases the environment that existed before the )1 command was issued. too many of them can clutter your directory. replacing it with the restored environment. We use the term environment to denote the set of all objects accessible at any one time. In response to this command.\ :~ i i ~\- :' \. This figure should match the figure written after the )s command. as new employees are hired or retire. a message indicating the number of bytes read in will be produced. slips of paper tend to get lost. Saving Environments Updating an employee database is not something you do once and then never again.' A Simple Application 49 newEmployee I newNumber I lid: printNoReturn.

A common use for this feature is for users to save an environment containing their favorite classes and objects that they have created. Note that the same environment can be loaded many times. and the user can continue as if neither the )s nor the )1 commands had been issued. In the scheme described in this chapter. How would this change the methods for this class? What are the advantages and disadvantages of both schemes? . Add messages to delete employees from the database.\ ~\. 4. Add checks to make sure a new employee record does not override an older one and that a request to update specifies a valid employee identification number. Change the class EmployeeRecord so that it maintains the identification number as part of the record. the application is rather lax about error checking. EXERCISES 1. This environment can then be quickly loaded. What changes are then required in EntployeeDatabase ? 5. for example. They are kept oilly as keys in the database and not as part of the record. Is it necessary to have both the messages initialize and upDate in EmployeeRecord ? Can one be replaced with the other? What changes would the user notice? 3. 2. As it stands. Instead of maintaining an instance of Dictionary in EmployeeDatabase. 50 The Language mand are now valid.. and the classes will be available without having to issue )i commands for each one. one could make it a subclass of Dictionary. identification numbers are different from the other fields in the employee record.

'.:~> CHAPTER / /// / / / / // '" '"'" '" '""" '" '''' "'. and Coercions »»>::»»> /////////.."- {{{{{{{{{{{{{{{{{{{{{{{{{i -}}}}}}}}}}}}}}}}}}}}}}}}} ««««{«({{««««{. '.»»>:»>:>:. t}}}}}}}}}}}}}}}}}}}}}}}}} :«««««««««««1 ))))))))))))))))\)))))) 51 . Cascades. 6 Primitives./ ~ {{{{{{{{{{{{{{{{{{{{{H{{ '" '" '" "" '" "" '" "" "-.

Since there is no necessity to name the intermediate object. When a cascade is evaluated. it can be used anywhere expressions are legal.' It is written as an expression followed by a semicolon followed by another expression without a receiver. . the first expression is computed. suppose you wanted to create a new instance of class Bag and to initialize it with the values 0 and 1. There are several advantages to using cascaded expressions. A cascade is a type of Smalltalk expression. and so on. for example. In other words. Thus. The result of a cascade of any depth is always the result of the first expression. Appendix 5 describes how cascades and primitives are handled in the Xerox system. add: 0 . the result of the second expression is ignored and discarded. a cascade obviates the need for a great many temporary variables.. ~\ 52 The Language Cascades Suppose you wanted to send a number of messages to the same object. for example. print in place of (Integer respondsTo: # + )print 1. since the entire sequence is an expression. One way to do this would be to use a temporary variable as in: i~Bag new i add: 0 i add: 1 Another way is to use a cascade. Since a cascade is an expression. and then used as the receiver for the second expression. as in: Bag new. For example. The result of a cascade is not the result of the second expression but the result of the first expression. as an argument. For example. the syntax and meaning of cascades and primitives are different in the two systems.. to print the result yielded by a keyword message you could write: Integer respondsTo: #+ . to any desired level. it can be used to form another cascade. Also. as is the result of the entire expression.\~ \) \ i ~ -. you can create a new Bag and initialize it all with one expression. Note that although similar concepts appear in both the Little Smalltalk system and the Xerox Smalltalk-80 programming environment. Finally a cascade can often be used in place of parentheses to separate two keyword messages or to apply one message to another of lower precedence. add: 1 The receiver for both the messages add: a and add: 1 is the result of the expression Bag new.

actions are produced by one object sending a message to another object.85 radians." if x is a radian with value 0. called a primitive. In response to this message. are very simple. for example: <lntegerAddition i j > Alternatively. Primitives. . a primitive is denoted by an angle bracket followed by a name followed by a list of objects followed by a closing angle bracket.1. permitting access to the underlying virtual machine. In any case. Cascades. the above expression could also be written as: <primitive 10 i j > However. In general the value nil is returned." This technique is used in many classes to provide more informative printed representations of objects. Thus. As described so far." Thus the message X print. there would seem to be a sort of infinite regress preventing any substantial action from taking place. and Coercions 53 Primitives At this point you may be wondering how anything ever gets accomplished in Smalltalk. A portion of the class description for the class Radian is given in Figure 6. The primitive name can be replaced by the keyword "primitive" and this number.85. A primitive can be thought of as a system call. If this were all there were to the language. There is no notion of a "receiver" in a primitive expression. For this reason most primitive operations. if arguments to a primitive are not correct or the primitive cannot produce the desired result. Note that a primitive call is not the same as a message passing. Primitives are chiefly used in class descriptions and should almost never by typed directly at the command level. the second object may in turn send still other messages to more objects. every primitive is also assigned a number. Syntactically. The solution to this problem is yet another form of expression. as the name suggests. since the integer addition primitive is number 10. There is a temptation on the part of many novice Little Smalltalk users to use primitives in place of Smalltalk expressions in the name of lleffi_ If . although the differences are largely hidden from the user. and a primitive cannot send further messages in producing its response. will produce the message "0. the second form is less representative of the operation being performed. and an error message produced. This class uses the primitives Sin and Cos to compute the value of the trigonometric functions on radian inputs. A table of primitives is given in Appendix 4. only the second form (expressing the desired primitive by number) is recognized at the command level.\. Notice how the class Radian responds to the message printString by concatenating the string representation of its value with the literal string "radians.'.

" This should be avoided. only after a great deal of thought as to the alternatives. radians I l ciency. and the notion does not fit quite smoothly into the object-oriented framework of the rest of Smalltalk. The syntax is a bit obscure. Thus primitives should be used only as a last resort. and only in extremely simple methods that put a more lfSmalltalk-like" cloak over the primitive call. Such gains in efficiency are usually negligible.54 The Language Figure 6.1 0 The class Radian I value I [ ClassRadian :Magnitude new: x value ~ x asFloat < arg t value < arg asFloat = arg t value = arg asFloat sin t <Sin value> cos t <Cos value> tan t self sin / self cos asFloat t value printString t value asString. More importantly. Numbers The class hierarchy for numbers could be described as follows: Object Number Integer /~ ------- Magnitude I ~ Char Float . the notion of primitive is really just a pragmatic device permitting some operations to be specified that could not otherwise be given in Smalltalk. if they exist at all.

is somewhat more efficient. l' <GenerqlityTest self aNumber> ifTrue: [self] ifFalse: [aNumber coerce: self] + ?Number i (self maxtype: aNumber) + (aNumber maxtype: self) To uiIderstand these methods. then followed by any others (including user-defined classes).the method for + in the class Integer looks something like the followin~: + aNumber l' <SameTypeOfObject self aNumber> ifTrue: [ <lntegerAddition self aNumber> ] ifFalse: t super + aNumber] The primitive SameTyPeOfObject tests whetherthe two objects given as arguments are instances of the same class. The class Float. namely Number. \ :' \ . In the class Number the following methods appear: If 11 maxtype: aNurnber. some regard this as a weak argument and would advocate the more direct and obvious Smalltalk expression over the less clear primitive call. consisting of Integer at the lowest level. Float. primitive IntegerAddition produces the integer sum of the two arguments.. the class Number chooses the object with the more general class and passes to it the message coerce: with the other object as an argument. since it does not require any further message passing. 2.\.5.~ Primitives. in this context the use of this primitive can be thought of as equivalent to: aNumber isKindOf: Integer although the primitive expression.5 results in the message coerce: 2 being passed to the object 3.. If the argument is not an instance of class Integer. Even so. The use of the primitive here is justified on the grounds that arithmetic operations are used much more frequently than other messages. If an argument is not of the correct type. the superclass. Number. followed by Float. and Number implements methods for the arithmetic operations. the message is then passed to the superclass. For example typing i + 3. . Cascades. Since the pseudo variable self is an instance of the class Integer. For example. the message + is passed to. The classes Integer and Float perform operations only for arguments of their own type. and any user-defined dasses. and Coercions 55 Each of the classes Integer. must implement a method for this message. When presented with two objects of different levels in the hierarchy.' \). 2 If the argument is of the correct class. consider that a hierarchy of number classes can be defined...

Rewrite the methods for = and < in class Radian so that radians can be compared only to other radians. The method for this message uses the primitive GeneralityTest. such as Float. and false otherwise. if you wish. and Float. Does the class Integer need to provide a method for coerce:? Why or why not? How about the class Number? 3. Assuming the response to coerce: was as expected. Examine the class descriptions for the classes Object. 2 + (3 pri nt) . add: 0. 2. There is a problem here in determining the relative generality of two user-defined classes. EXERCISES 1. 4. What will the result of typing the following expression be? Explain why. 5. which returns true if the first argument is of a more general class than the second. Integer. add 1) How else might this be done in a single expression without using cascading? You can use temporary variables. For example the initialized Bag discussed in the text could be used as an argument to another object as follows: anObject foo: (Bag new. 3 The method for maxtype: therefore either returns its first argument or coerces the argument into being of the class represented by the second argument: coerce: aNumber i aNumber asFloat When Number has coerced both arguments into being the same type. Explain in detail how radians respond to the messages < = and > = . One of the projects described at the end of the book invites the student to examine this problem and produce more general solutions. . One advantage cited for cascaded expressions was that several Smalltalk statements could be combined together into one expression. + 4 3.56 The Language To find the most general form for the operation you use the message maxtype:. Number. and the expected result is finally produced. Magnitude. or even the generality of user-defined classes and known classes. the objects should now be able to respond correctly to this message. the original message is then passed back to the modified objects.

the second argument is evaluated. Discuss the advantages and disadvantages of this approach. . The message test:do: uses blocks for both arguments. Cascades. Define the imaginary part of the current number to be the argument. Using cascades and blocks we can implement a multiway switch in Smalltalk. define the class Complex used to manipulate complex numbers. if it returns true. if no previous condition has been met.6. The first argument. Coerce a non-complex. and. . a one parameter block. 7. imagpart: self That is. a number will create a new instance of the type Complex and initialize it with the current object. the second argument (a block) will be evaluated.'.~ . Assume that the class Number contains the following method t Complex new. if equal. A useful control structure in many programming languages is a multiway switch statement. Test your class description by typing several example expressions involving complex numbers. and Coercions 57 . 8. ~(} \) \ ~ '. Your class should implement methods for the following messages: new realpart: imagpart: coerce: + * printString Set both the imaginary and real portions of the complex number to zero. is evaluated using the selection key. Recall that in the hierarchy of number classes all other classes have a higher ranking then any of Integer or Float. a block. An alternative to having instances of class Radian maintain their value in an instance variable would be to make the class a subclass of class Float. The message default: executes the argument. in response to the message i. Complex multiplication.0:- Primitives. Complex addition. A flag is maintained by each instance of Switch indicating whether any condition has been satisfied. The message case:do: will compare the first argument to the selection key. produce a printable representation of the number. Using this method. Define the real part of the current number to be the argument. returning an equivalent complex number with zero imaginary part. which permits the selection of one out of several possibilities based on the value of some selection key. The class Switch will use the message new: both as a creation message and to assign the switch selection key.

58 The Language For example. the following statement uses a variable suit representing the suit of a card from a deck of cards. Provide a class description for Switch. case: #c1ub do: [ color~lblack' ] . It places into color a string representing the color of the card. . default: [ self error: 'unknown suit suit] I ."\ . Switch new: suit.\. case: #spade do: [ color~lblack'] . test: [:x I (x = #diamond) or: [x = #heart] ] do: [ color~'red'] .

} \ s " ~\ CHAPTER // //// / /~// """" "" ".}}}}}}}}}}}}}}}}}} }}}}}}}" ««({««««««««(.:. '" """" '" "".\ ~} \ .::>."""'''''''''''''''''''''''''' ~{{Hi{U{{{{{{{{{{{{{{{{{ n}}}}}}}}}}}}}}}}}}}}}}}} [««{««««««({{«.»> ////////// . 7 A Simulation :»»>~:>.. . "-i {{{{{{{{{{{H{{{{{{{{{{{{ . i)))))))))))))))))))))) 59 .

\ ~~ \. Each event is marked with a time at which the event should take place.r· ). that of a large number of independent objects communicating via message passing. The Ice Cream Store Simulation In writing a simulation. such as in the ice cream store. This chapter will illustrate the construction of a simple simulation describing the operation of an ice cream store. and very simple. A pending event is defined as any event that has. Indeed. A "clock" records the current "time. that there is never more than one pending event. a simple counter will suffice. is particularly suitable for constructing simulations of processes that can be described as the interaction of a finite number of events. This means that the actions of the simulation will revolve around the recording and processing of a finite number of individual events. The reader interested in more extensive simulation techniques can consult some of the references listed at the end of this chapter. the first question to ask is what the different objects in the simulation should represent and what functionality they should possess. The application we will describe is known as a discrete. Assume. Each object can represent some object in the simulation model.- \ ~ -. The class maintains the current "time" in the variable currentTime. The message proceed will indicate that the next action should take place. The first version of the class Simulation is shown in Figure 7. We can isolate the actions of the simulation that are independent of any particular application in an abstract superclass called Simulation. first. Particular simulations. but only that it can be represented by an object. with the local memory of the object maintaining information about the state of the model object. will then be subclasses of Simulation.' 60 The Language The programming paradigm employed in Smalltalk. our simulation must involve at least two classes of objects: a class representing the actions of the various customers that come to the store and a class representing the actions of the store itself in response to the requests of the customers.1. event-driven simulation. not yet taken place. such as our simulation of an ice cream store.r . the occurrence of each event controls the sequence of further actions in the simulation. Our first. Messages correspond to interaction between the simulation objects. At the moment we need not concern ourselves with how each event is to be encoded. Since the . the language was developed largely with just such applications in mind. At its simplest. An event is simply the occurrence of some action that is important in the context of the simulation. attempt at simulating an ice cream store will illustrate these concepts." which need not be related in any way to conventional time. As time progresses.

how often do customers arrive. Having described the framework for keeping track of the simulation "bookkeeping. such as a new customer will arrive every two minutes and order three scoops of ice cream. order some number of scoops of ice cream. For example. Instead. The message processEvent: must therefore be recognized in each subclass. and leave. however.1 0 The class Simulation (version 1) I currentTime nextEvent nextEventTime I [ new currentTime ~ 0 time Class Simulation t currentTime add Event: event at: eventTime nextEvent ~ event. The phrase "uniformly chosen" means that each outcome (or each time) is equally likely. how many scoops of ice cream will each customer order. For example." we can go on to describe the actions of our ice cream store. We could make a simple assumption. proceed leaves the task of interpreting the event to a subclass by passing the message processEvent: to the pseudo variable self. however. nextEventTime ~ eventTime proceed currentTime ~ nextEventTime. Each scoop of ice cream produces some amount of profit for the store. not sufficient to merely describe what actions take place in the store. it is more interesting (to say nothing of being more realistic) to define the outcome of an event in terms of a random value chosen from a selected distribution. Such a deterministic assumption. defeats the necessity for the simulation at all since we can easily predict the outcome: in 15 minutes seven customers will arrive and order 21 scoops of ice cream. and so on. and thus we can use a random number generator to select . let us suppose that at each instance the next customer will appear at a time uniformly chosen from the numbers 1 to 5. At the end of the simulation period we will want to know the profit. we must also describe how often those events should take place.\ ~\- \ ~\ - A Simulation 61 Figure 7. It is. self processEvent: nextEvent actual interpretation of an event will differ from one simulation to the next. We start with a simple model in which customers arrive.

For our simulation to work.Random new. Letting IceCreamStore be the class representing our simulation of the store. all that is required is determining the amount of ice cream to be dispensed (and thus the profit to be made) and scheduling the next customer. self scheduleArrival scheduleArrival self addEvent: Customer new at: (self time + (rand rand Integer: 5» reportProfits ('profits are I. Note that the instance variable rand is initialized to be an instance of the random number generator. The only remaining part of our simulation is deciding how the class IceCreamStore should respond to the message processEvent:.62 The Language Figure 7. and that during initialization the first customer is scheduled. In our first simulation very little functionality is required of the customer. but is important. profit) print a value in this range to represent the time for the next arrival. Notice we have chosen to represent events by an instance of class Customer. each event must generate some number of other events. Thus the class shown in Figure 7. The latter requirement may not be obvious.2. rand ('. this could be computed as shown in the method for the message scheduleArrival in Figure 7.2 0 A portion of the class IceCreamStore (version 1) Class IceCreamStore :Simulation I profit rand I [ new profit ('. but must be made someplace. The message randomize is used to insure that a new random sequence is generated each time we invoke the simulation. rand randomize. .3 suffices for customers.O. Since the event is an instance of class Customer. Each customer must arrive and upon arrival decide the number of scoops of ice cream he or she will order. The decision about where and when each event should schedule the next is not always obvious.

A Simulation 63 Figure 7.17 ). The response to the message processEvent: can then be represented as follows: processEvent: event ('customer received at I.3 0 The class Customer (version 1) Class Customer I rand I [ new rand ~ Random new. we could then run our simulation as follows: store ~ IceCreamStore new [store time < 15] whileTrue: [store proceed] customer received at 1 customer has 1 scoops customer received at 2 customer has 3 scoops customer received at 5 customer has 1 scoops customer received at 8 customer has 1 scoops customer received at 11 customer has 1 scoops customer received at 16 customer has 2 scoops store reportProfits profits are 1. To determine how much profit might be produced in 15 minutes. profit ~ profit + (event numberOfScoops self scheduleArrival * 0.53 . l' number I Let us assume that each scoop of ice cream generates seventeen cents profit for the store. rand randomize numberOfScoops I number I number ~ rand randlnteger: 3. self time) print. ('customer has " number I scoops I) print.

[ value> (weights at: index) ] whileTrue: [ value ~ value . an instance of this class will return a value between 1 and the size of the weights array. rand randomize. This is certainly a far different distribution from the one given by our assumption that all three outcomes are equally likely. we return 3. Do customers really arrive with a uniform distribution? Do they always arrive individually? Do they really order some number of scoops with a uniform probability? Let us tackle the last question first. we return 2. and only 10% of the time do they order three scoops. having produced our first simulation. let us go back and examine some of the assumptions we made to decide if they are really accurate. if less than or equal to 90. We can generalize this to work for any weighted distribution we like. In order to simulate this behavior. If this uniform value is less than or equal to 65. A distribution of values such as we have described is known as a weighted discrete probability. max ~ anArray inject: 0 into [:x :y I x next I index value + y] I value ~ rand randlnteger: max. distributed according to the weights given. In response to the message next. two 25% of the time.4. otherwise. Figure 7. and three 10% of the time. One way to generate a random value satisfying our requirements is to generate a uniform random number between 1 and 100. index ~ index + 1 ].64 The Language Now. Suppose we observe a real ice cream store for some period of time and note that 65% of the time customers will order one scoop. producing the class DiscreteProbability shown in Figure 7.(weights at: index). rand ~ Random new. we must generate a random integer that returns one 65% of the time. we return 1. index ~ 1. 25% of the time they order two scoops. j index .4 D The class DiscreteProbability I weights rand (I ass DiscreteProbabil ity max I [ defineWeights: anArray weights ~ anArray.

they will take seats and start to look at the menu. which will thereafter be returned in response to a request via the message groupSize. Each instance of class Customer will be changed. Still later.6. since it is a social process. Upon creation. There are several more factors to consider. Let us alter the assumption that customers arrive one by one. One convenient data structure to maintain both the pending events and their time for initiation is a dictionary using the time as a key and the event as the value. Some group of customers will arrive. Otherwise. In response to the message proceed. Suppose we complicate things now by adding an inside dining area to our ice cream store. one group can order while another arrives). However. the group will have finished their ice cream and will leave.A Simulation 65 The distribution of ice cream orders was obtained by obsenring a large body of customers. now we can only accommodate those customers who can find chairs. One group of customers can be eating their ice cream. to represent a group of individuals. The following simulation will illustrate the second variation. the event with the next smallest time marker is removed from the queue and initiated. So we can argue whether the number of scoops an individual will order should be part of the protocol for the Customer class (since the customer is issuing the order) or for the IceCreamStore class (since the distribution is taken from observations of ice cream store customers as a group).remember not only a . If there are not enough chairs. Events are now more complicated. while another is ordering. In this new situation. Our revised class Simulation is shown in Figure 7. and a third is just arriving. two events can happen at the same time (for instance. but must be a set of events. people tend to eat ice cream in groups. Whereas formerly we assumed we could accommodate as many customers as would arrive in any particular time period. Later the group of customers will place an order and receive their ice cream. where the only event of importance from the customer's point of view was the receipt of the ice cream. The last simulation illustrated the first Variation. Now the class Simulation must be altered to keep a queue of pending events. Furthermore.5. Now the following sequence will take place: 1. 3. Thus. Therefore the value of the dictionary cannot be simply a single event. our second simulation can be given as shown in Figure 7. therefore. Given these changes. we can now have several events happening simultaneously. each instance will determine its group size. the basic assumption that there is always just one pending event is no longer valid. they will immediately leave. We must . the sequence of events affecting a single customer or a group of customers is now more complicated than the previous case. 2.

66 The Language group of customers but also what state they are in: whether they have just arrived. Thus. group. or are about to leave. self scheduleArrival scheduleArriva I self addEvent: Customer new at: (self time + (rand randlnteger: 5» processEvent: event Ccustomer received at I. scoopDistribution ~ DiscreteProbability new scoopDistribution defineWeights: #(65 25 10). group timesRepeat: [number ~ number + scoopDistribution next]. self time) print.5 0 Ice cream store simulation (version 2) I profit rand scoopDistribution I [ Class IceCreamStore :Simulation new profit ~ O. rand ~ Random new. are about to order. the protocol for scheduleArrival might be given as follows: scheduleArrival I newCustomer I newCustomer ~ Customer new. Recall that a block evaluates in the context in which it IS defined and not until passed the message value. t number I I Program Continued . profit ~ profit + «selfscoopsFor: event groupSize) self scheduleArrival * 0. for example.17). ('group of I. self addEvent: [self customerArrival: newCustomer] at: (self time + (rand randlnteger: 5» Figure 7. I have I number I scoops I) print. One way to do this is to store as an event a block which when evaluated will move the customer to the next state. scoopsFor: group I number I number ~ O.

Since this block references the temporary variable newCustomer. This takes place when the event is processed: processEvent: event event value. the storage it uses ·will not be reclaimed) as long as the block exists. profit) print Class Customer 1 groupSize I [ new groupSize <E- (Random new randomize) randlnteger: 8 groupSize t groupSize A new customer is created and placed in the temporary variable new Customer.~ A Simulation 67 reportProfits ('profits are I.e. A block is then installed in the event queue.\~. self scheduleArrival Figure 7.. However. cu rrentTi me ~ 0 time t currentTime addEvent: event at: eventTime Program Continued . The processing indicated by the block will not take place until the block is evaluated using the message value.6 0 The class Simulation (version 2) Class Simulation I cu rrentTi me eventQueue [ I new eventQueue ~ Dictionary new. the temporary will be retained (i. each time the message scheduleArrival is received. a new instance of Customer will be created.

'take chairs. eventset remove: event. event ~ eventset first. When evaluated. The protocol for customerArrival: can then be given as follows: customerArrival: customer I size I size ~ customer groupSize. currentTime ~ minTime. 68 c= The Language ---l (eventQueue inciudesKey: eventTime) ifTrue: [(eventQueue at: eventTime) add: event] ifFalse: [eventQueue at: eventTime put: (Set new. self addEvent: [self customerOrder: customer] next: (rand rand Integer: 3).size. this block will pass the message custonlerOrder:. to the simulation object. (eventset isEmpty) ifTrue: [eventQueue removeKey: minTime]. I arrives ') print."\ ~. ('group of size 1. eventset ~ eventQueue at: minTime ifAbsent: [t nil]. Upon arrival. size. schedules order' print. if there are a sufficient number of chairs. the customers sit down and order. otherwise they leave. The protocol for this message is as follows: . add: event)] add Event: event next: timelncrement self addEvent: event at: currentTime proceed + timelncrement I minTi me eventset event I minTime ~99999. it will send the message customerArrival: to the pseudo variable self. Let us use an instance variable remainingChairs to represent the number of free chairs at any point.} \. that is. eventQueue keysDo: [:x I (x < minTime) ifTrue: [minTime ~ xl]. leaves· print] Notice that again a block has been used to represent the next event. The time between arriving and ordering will be a random value from one to three. self processEvent: event When the block created in scheduleArrival is evaluated. (size < remainingChairs) ifTrue: [remainingChairs ~ remainingChairs . ] ifFalse: ['finds no chairs.

For example.7 shows one class that can be used for generating random values with a normal distribution. Figure 7. . we can change our assumption about customers arrivals to be more realistic. ('group of size I. size. Given the ability to produce random values from a normal distribution. few random events ever occur with uniform probability. such as a Bernoulli distribution or a Poisson distribution. After closing time. they relinquish their chairs. One very common form is the Normal distribution. remainingChairs ~ remainingChairs + customer groupSize We will make one final change to illustrate how our simulation can be made even more realistic. size. size timesRepeat: [numScoops ~ numScoops + scoopDistribution next]. no new customers will arrive. profit ~ profit + (numScoops * 0. other distributions. but the customers waiting to order and waiting to leave will still be processed. When the customers finally do leave. numScoops ~ O. In the method for scheduleAnival we have also incorporated a "closing time" by adding events corresponding only to customers who arrive before some fixed limit. we could assume customers arrive in a normal distribution with a mean of 3 minutes and a standard deviation of 1 minute. This is so that the event queue can be flushed out and the simulation terminate in a clean fashion. "\ ~ . More often. I SCOOpSl) print. ('group of size I. which is characterized by values clustering around a mean."\ ~\ ~. I orders I .8 shows the class header and the protocol for the initialization message new and the message scheduleArrival incorporating these changes. Figure 7. self addEvent: [self customerLeave: customer] next: (rand rand Integer: 5) Once more the time between a group of customers ordering and leaving is determined by a random value chosen between 1 and 5. In practice. Other random values used in the simulation could be modified to use a different distribution by making changes such as the ones we have illustrated for the arrival time distribution. with the chances of a value decreasing exponentially the farther they move from the mean. an interested reader can refer to the end of this chapter fDr additional literature. numScoops . . No attempt is made to motivate the algorithm used in the method for the message next. I leaves l) print. are observed to model a process. customerLeave: customer I size I size ~ customer groupSize.17).\ \): A Simulation 69 customerOrder: customer I size numScoops I size ~ customer groupSize.

u ~ (.0 deviation: 10. time ~ self time + (arrivalDistribution next).0 * s In / s) sqrt. self scheduleArrival scheduleArrival ~arrivalDistribution ~ I newCustomer time newCustomer ~ Customer new. I Program Continued .7 0 The class Normal I mean Class Normal :Random deviation I [ new self setMean: 1.1. t mean + (deviation * v1 * u) Figure 7. deviation ~ s next I v1 v2 sui s~l [s > = I] whileTrue: [v1 ~ (2 * super next) . rand ~ Random new.0 deviation: 0.8 0 The class IceCreamStore (version 3) Class IceCreamStore :Simulation I profit arrivalDistribution rand scoopDistribution remainingChairs [ new I profit ~ O.1 v2 ~ (2 * super next) .70 The Language We end with an example session of our final simulation: Figure 7. remainingChairs ~ 15. Normal new: setMean: 3.2. s ~ v1 squared + v2 squared ].5 setmean: m deviation: s mean ~ m. scoopDistribution ~ DiscreteProbability new: defineWeights: #(65 26 10).

~} \ > \): \ A Simulation 71 (time < 15) ifTrue: [ self addEvent: [ self customerArrival: newCustomer] c:lt: time] store ~ lceCreamStore new [stQre time < 60] whileTrue: [store proceed] event received at 3. leaves Program Continued . leaves event received at 12.9123 group of size 1 orders 1 scoops event received at 10. schedules order event received at 11.0194 group of size 2 arrives finds no chairs.~~. leaves event received at 14. leaves event received at 6.46877 group of size 8 arrives takes chairs.91228 group of size 1 arrives takes chairs.eived at 5. schedules order event received at 12.9123 group of size 1 leaves event received at 12.6301 group of size 5 arrives finds no chairs.9499 group of size 7 arrives takes chairs. schedules order event re<.8463 group of size 5 orgers 6 scoops event received at 12.46877 group of size 8 orders 11 scoops event received at 146877 group of size 8 leaves event received at 8. schedules order event received at 10.8077 group of size 7 arrives finds no chairs.8463 group of size 5 arrives takes chairs.81336 group of size 8 arrives finds no chairs.9499 group of size 7 orders 13 scoops event received at 13.

for example.27 Since Smalltalk objects can be created. A large collection of references to computer games and simulation exercises is found in (Gibbs 74). EXERCISES 1.9499 group of size 7 leaves event received at 17. and GPSS (Greenberg 72). 2).} . and in general placed in a one-to-one correspondence with objects in the abstract model being simulated (the objects representing the customer groups. Other programming languages designed for simulation include Simula (Birtwistle 73). Vol. Demos (Birtwistle 79). An argument can be made that DiscreteProbability should really be a subclass of Random. it is easy to modify. Deciding when to use subclassing and when to use an instance variable is not always easy. Further Reading A good introduction to the concepts of simulation can be found in (Maryanski 80). such as the following: Class DiscreteProbability :Random I weights max I [ Program Continued . \ \ " 72 The Language event received at 16. A more theoretical treatment is given in (Zeigler 76). . The definitive description of the Smalltalk-80 language (Goldberg 83) illustrates many more extensive simulations in Smalltalk. removed.'. once a simulation has been developed.8463 group of size 5 leaves store reportProfits profits are 5. replacing a value generated with a normal distribution with one generated according to a Bernoulli distribution. for example). Finally. The algorithm for computing the normal distribution is taken from Knuth (Knuth 81. Similarly the development of new simulations is simplified by the inheritance of common behavior from class Simulation. It is relatively easy to take any model expressed in the discrete event-driven form and enunciate it in Smalltalk.

"\ .(weights at: index). and then describe why this class description will not produce the desired result. index ~ 1. For example. . [value> (weights at: index)] whileTrue: [value ~ value . a sample space might represent a deck of cards. 3. index ~ index + 1]. suppose a group of boys are observed to have heights represented by the array #(60 54 60 62 50). random selection without replacement. The following shows how a class SampleSpace might be used for this purpose: sample ~ SampleSpace new. A Simulation 73 defineWeights: anArray weights ~ anArray.':0:. II The alternative. We can then ask for the height of a randomly selected boy. t index Look at the class description for Random. This card should then be thought of as being removed from the deck and not available for return in subsequent selections. the choosing of a card. For example. define: #(60 54 60 62 50) sample first 60 Producy a class description for SampleSpace. 2. is frequently more useful. Describe how to modify the class SampleSpace to provide random selection without replacement. to the message randlnteger:. If written in a manner analogous to DiscreteProbab ility. and a random selection. max ~ anArray inject: 0 into: [:x :y I x + y] next I index value I value ~ super randlnteger: max. in particular the response . the class SampleSpace defined in the last exercise is said to provide Ilrandom selection with replacement. An alternative method of defining a discrete probability is to provide the actual sample space in a collection.

\ .'" '" "'" -}}}}}HH}}}}}}}}}}}}}}}} "- ««««««««««{«. '" '" '" '" '{{{{{H{{{{ {{{{{{{{{{{{{{ r}}}}}}}}}}}}}}}}}}}}}}}}} ((((((((((((i ))))))11111)))))))))\\) 74 . \:"': .. "'. ~ . '''-. " "-" '" '" '" "'. "'.".. /////////// CHAPTER '{{{{{H{{{{{{{{{{{{{{{{{{....\. 8 Generators // / / // //// / "'."'.

This functionality is provided by class ArrayedCollection.1. and thus the response to first and next is merely to enumerate their elements. . only the values are retrieved from an external disk as required. such as instances of Interval. Instances of File are similar.~ \ ~~ \ '. using an instance variable current (Figure 8. Indeed. String. Instances of Array or String return the element stored in their first subscript position (if they have at least one subscript position) in response to the message first. On subsequent next messages they respond with the remaining elements in order. such as instances of the class Bag. (current < = self size) ifTrue: [ self at: current) .\ . can all be considered to be generators.1· 0 A portion of the class ArrayedCollection I current I [ first i next current ~ 1.\ ~\ Generators 75 In Little Smalltalk. maintain their collections in memory. (current < = self size) ifTrue: [ self at: current] i current ~ current + 1. such as Array. or the special value nil if there are no elements in the collection.2). and that recompute each new element on demand (Figure 8. the term generator describes any object that represents a collection of other objects and that responds to the following two messages: first next The response should be an element of the collection. or nil if there are no more elements in the collection. and thus the order in which elements are produced in response to first and next messages is not defined. do not possess a "natural" ordering. Notice that nothing is said about how a generator produces the object to be yielded in response to one of these messages. instances of the standard data structures. For example. or Bag. the list of elements represented by instances of class Random can Figure 8. other than that all elements are eventually produced and no element is produced more than once. such as instances of Bag or Array. maintain only the information necessary for generating each new element as required. The response should be another element in the collection. Other generators.) Some data structures. Some objects.

to tell if a number nl is a prime. itself and 1. i (self inRange: current) ifTrue: [ current] be considered to be infinite in length. Thus. From the point of view of the message passing interface. only those values that are prime. Therefore a simple generator for primes can be constructed by merely retaining the previously generated primes in a Set. a prime number is a value having only two divisors.2 0 A portion of the class Interval Class Interval :Seq uencea bleCollection I lower upper step current I [ inRange: value i (step strictlyPositive) ifTrue: [ value between: lower and: upper] ifFalse: [value between: upper and: lower] first current ~ lower. . Consider the problem of producing prime numbers. and successive prime numbers in response to each next message. and thus cannot be stored entirely in memory."\ \ " . Even in cases where the sequence to be produced in response to first and next is finite.. an object representing the last prime produced is incremented and tested until a value having no factors is found. The new value is then inserted into the set and returned. then the prime factors of n must also divide nt. there is no distinction between classes that iterate over their elements in memory and classes that produce new elements on demand..:} ~\ " ~\ 76 The Language Figure 8. By definition. A generator for prime numbers will produce the first prime value (namely 2) when offered the message first. we need not test all values less than 111. i (self inRange: current) ifTrue: [ current] next current ~ current + step. An example will illustrate how generators assist problem solving in Smalltalk. As each new value is requested. there may be advantages to computing elements as needed rather than all at once when the object is defined. If a number n divides a number 111.

Similarly three is a factor of one third of all numbers. prevPri mes add: (IastPri me ~ 2). In fact. prevPrimes add: lastPrime.\ . rather than add:. prevPrimes add: (IastPrime ~ 2). Generators 77 Class Primes I prevPrimes lastPrime I [ first prevPrimes ~ Set new. The loop in the method for testNumber: haIts and returns as soon as a previous prime is shown to be a factor of the number under consideration. l' lastPrime next [ lastPrime ~ lastPrime + 1. and so on. which adds elements to the end of the list.'l:. The appropriate data structure for an ordered collection without keys is a List. keeping the previous primes in order allows yet another improvement in the algorithm since we can terminate the search of previous primes as soon as a value larger than is reached where n is the value being tested. l' lastPrime testNumber: n prevPrimes do: [:x I (n " "x l' true = 0) ifTrue: [ l' false] ]. A few simple observations will improve the efficiency of this algorithm and will also illustrate the proper choice of data structures. in the order in which they were generated) we would on average remove nonprimes much more quickly than the more or less random order given to us by a Set. l' lastPrime next [ lastPrime ~ lastPrime + 1. If we could arrange to test previous primes in numeric order (that is. Two is a factor of exactly one half of all numbers. self testNumber: lastPrime ] whileFalse. Thus we rewrite the algorithm to use a List and the insertion method addLast:. . vn Class Primes I prevPrimes lastPrime I [ first prevPrimes ~ List new. which would add to the front of the list.

prevPrimes addLast: lastPrime. (n "" "" x 0) ifTrue: [ t false l l = An obvious problem with both of these prime number generators is that they require an ever-increasing amount of storage to maintain the list of previous prime numbers. An alternative.'. Class Primes I lastPrime I [ first t lastPrime ~ 2 next [ lastPrime ~ lastPrime + 1. This is analogous to a recursive procedure in programming languages such as Pascal.. (n "" "" x = 0) ifTrue: [ i false l l You may have noted that the message do: is being passed to an instance of class Primes. self testNumber: lastPrime l whileFalse. do: aBlock I item I item ~ self fi rst. is a recursive generator. [ item notNii l whileTrue: [aBlock value: item.. which does not contain a method for this message.\ . the size of this storage could easily become a problem. t lastPrime testNumber: n . The following program does not maintain the list of previous primes but instead regenerates the list each time a new number is to be tested. t lastPrime testNumber: n (Primes new) do: [:x I (x squared> n) ifTrue: [ t true l. The method for do: is inherited from class Object and is defined in terms of first and next. item ~ self next i nil l. prevPrimes do: [:x I (x squared> n) ifTrue: [ t true l. 78 The Language self testNumber: lastPrime l whileFalse. If you were constructing a long list of prime values. . which trades slightly longer computation time for reduced storage.

however. First an instance of Interval that will generate all numbers from 2 to some fixed limit is constructed. examining only the messages to which an object responds) a filter looks just like a generator. that is frequently useful in conjunction with generators. Class FactorFilter I myFactor generator I [ remove: factorValue from: generatorValue myFactor ~ factorValue.response to next (the message first is in this case replaced by the initialization protocol). i nil t = Using FactorFilter. Unlike a "true" generator. response to first or next but takes values produced by a previously defined generator and modifies them or filters but values. \ ~( . Any object can be manipulated as a generator merely by providing methods for the messages first and next. As each value is removed.\ \ . values from the underlying generator are requested andretumed.. In . except values for which the given number is a factor are repressed.0) ifTrue: [ possible]]. a filter does not produce new· values in. with the exception that values for which the given number is a factor are filtered out. Filters An entirely different program can solve the same task as the prime number generators described in the last section. filters. It uses another programing technique.> Generators 79 The fact that do: is in class Object and therefore provides functionality for all objects illustrates the peIVasive nature of generators in Little Smalltalk. Instances of FactorFilter are initialized by giving them a generator and a specific nonnegative value. The class FactorFilter exemplifies some of the essential features of a filter. a filter is inserted in front of the generator to insure that all subsequent multiples ."\ . Extemally(that is. Thus the sequence returned by an instance of FactorFiiter is exactly the same as that given by the underlying generator. generator ~ generatorVa lue next I possible I [ (possible ~ generator next) notNil ] whileTrue: [ (possible" " myFactor . you can construct a simple generator for prime numbers.

)~ \ '\ . this time for the last prime value returned. lastFactor ~ primeGenerator first. all the primes are eventually generated. the underlying generator constructed by the first occurrence of the message next can be viewed as follows: 2 fi Iter +---I oE-----! 2 to: n generator 1 When asked for the next prime. timings of actual programs show that the filter program is the fastest of the prime number generating programs described in this chapter. primeGenerator next ~ Pictorially. like the first two programs in the last section. the storage required for the chain of filters is proportional to the number of primes generated so far. Class Primes I primeGenerator lastFactor I [ first primeGenerator ~ 2 to: 100. Each time a new prime is requested. ~-i n filter 1+-(-Of course. the number 3. a filter is constructed to remove all factors of the previous prime. the generator is modified by adding a filter. in this fashion. remove: lastFactor from: primeGenerator). A new value is then requested from the updated generator. i lastFactor next primeGenerator i lastFactor ~ (FactorFilter new. .' \~\ 80 'I- The l.£mguage I -----------' of the value will be eliminated. The program continues. Despite this.

Generators 81 - Goal-Directed Evaluation A useful programming technique when used in conjunction with generators is goal-directed evaluation.1. By this technique. We first observe that in any solution. In general terms. The goal of instances of FactorFilter. however.3 0 A solution to the eight queens problem 1 1 2 Q Q Q Q Q Q Q Q 234 5 678 3 4 5 6 7 8 . no two queens can occupy the same column. Otherwise. and goal directed evaluation. filters. In a certain sense the notion of filters we have just described represents a simple form of goaldirected evaluation. Find an acceptable solution for column n . there is no acceptable solution.3). our approach will be to place queens from left to right (the order in which we assign numbers to columns). return nil. we can formulate the problem of finding an acceptable solution in column n recursively. place the queen for column n in row 1. We can therefore assign a specific column to each queen at the start. for example. a generator is repeatedly queried for values until some condition is satisfied. Go to step 2. and that no column can be empty. In this section we will describe how such a problem can be formulated and solved using generators. Figure 8. and reduce the problem to finding a correct row assignment for each of the eight queens. In the more general case of goal directed evaluation. Before that. Once we have found an acceptable solution in column 8 we are finished. the condition frequently involves the outcome of several generators acting together. as follows: 1. If there is none. An acceptable solution for column n is one in which no queen in columns 1 through n can attack any other queen in those columns. Consider the problem of placing eight queens on a chess board in such a way that no queen can attack any other queen (Figure 8. is to find a value from the underlying generator for which the given number is not a factor. An example will illustrate this Il1ethod.

If not. for example.1. Test to see if any queen in columns 1 through n . Of course.82 The Language 2. Find the next acceptable solution for column n . then an acceptable solution has been found. We represent each queen by a separate object. go to step 4.1 can attack the queen in column n. place the queen for column n in row 1 and go to step 2. whereas responding to next corresponds to starting in step 3. With this skeleton. neighbor ~ aQueen . Each queen maintains its own position in a pair of variables and also a pointer to the immediate neighbor on the left. our eight queens can be initialized as follows: lastQueen ~ nil (1 to: 8) do: [:i IlastQueen ~ Queen new. If there is none.4. corresponds to the following method: first (neighbor notNil) ifTrue: [ neighbor first ]. Step 1. We have already described our algorithm in terms of finding the first acceptable position and finding the next acceptable position. all positions are acceptable in column 1. otherwise. i self testPosition Figure 8. 4. setColumn: i neighbor: lastQueen ] Following the execution of this code the variable lastQueen points to the last (rightmost) queen. If the queen for column n is in row 8. Responding to first corresponds to starting in step 1. A skeleton for the class Queen is shown in Figure 8. It is therefore easy to apply our generator paradigm (using the messages first and next) to this situation. otherwise advance the queen by one row and go back to step 2. return nil. 3. then go to step 3. row ~ 1. if the queen can be attacked.4 0 The class Queen Class Queen I row column neighbor I [ setColumn: aNumber neighbor: aQueen column ~ aNumber. an instance of class Queen.

- ~~ \:. next (row 8) ifTrue: [ «neighbor isNil) or: [ neighbor next isNii ]) ifTrue: [ i nil]. Otherwise.column. until the leftmost queen is reached. otherwise. If the neighbor queen can attack. Before describing the method for this message. row ~ row + 1. Since we know the current queen is in a column different from the queen under test. it will return false.:. ~\ \.. «(row testRow) or: [ row + columnDifference = testRow] ) or: [ row . an explicit message (testPosition) is used to perform step 2. and so on. i self testPosition = All that remains is to test a position to see if any queen to the left can attack.\ ~ 'i. As we have already noted. testPosition (neighbor isNil) ifTrue: [ i self]. (neighbor checkRow: row column: column) ifTrue: [ i self next] ifFalse: [ i self] We have reduced the problem to the much simplier one of each queen taking a pair of coordinates for a queen positioned to the right and responding whether it or any queen to the left can attack that position. we describe the method used to find the next acceptable position. it will return true. any position is acceptable to the leftmost queen.columnDifference testRow]) ifTrue : [ i true]. it will pass the message on to its neighbor. row ~ 0]. it can be attacked only if it is in the same row or if the differences in the columns is equal to the differences in the rows (Le. we will pass a new message to the neighbor queen asking if it can attack the position of the current queen. (neighbor notNil) ifTrue: [ i neighbor checkRow: testRow column: testColumn ] ifFalse: [ i false] = = A final method is useful for producing the answer in a visual form: . checkRow: testRow column: testColumn I column Difference I columnDifference ~ testColumn . a diagonal). Notice the recursive use of the message next to find the next acceptable position.: Generators 83 Rather than falling directly into step 2 as we did in the informal description of the algorithm. which is a combination of steps 3 and 4 in our description. If the leftmost queen cannot attack.

row) print Putting all the methods for class Queen together. for example. The message select:. As noted in chapter 3. we could type the following example script: lastQueen ~ nil. \ ~\ "\ " 84 The Language printBoard (neighbor notNil) ifTrue: [ neighbor printBoard ]. ' row'. constructed a new collection consisting of only those elements from the original collection that satisfied some property..\ ~} . Thus (1 to: 10) select: [:x I x '"'" 2 = 0] produced the collection consisting of the even numbers less than 10. setColumn: i neighbor: lastQueen ] lastQueen first lastQueen printBoard column 1 row 1 column 2 row 5 column 3 row 8 column 4 row 6 column 5 row 3 column 6 row 7 column 7 row 2 column 8 row 4 lastQueen next lastQueen printBoard column 1 row 1 column 2 row 6 column 3 row 8 column 4 row 3 column 5 row 7 column 6 row 4 column 7 row 2 column 8 row 5 Operations on Generators Chapter 3 discussed several messages that could be used in conjunction with subclasses of Collection to form new collections in various ways. (1 to: 8) do: [:i IlastQueen ~ Queen new. ('column " column. the way most . if all we consider is the message passing interface there is no way to determine whether an object in response to first and next is merely looping over values contained in memory.

such as select: and reject:. The method used in class Collection and similar subclasses to respond to these messages is to form a new collection. In order to provide a common functionality to the various forms of generators we define methods for these messages in a new class. iterate over the receiving collection gathering the new elements. produce the appropriate elements. and transformation operators. merely 'making our generator a subclass of class Collection will not suffice. Finally a cross-product is produced by considering all pairs of values from the two sequences. when called upon. A shuffle is an intermixing of the values from the two sequences. A dot-product produces a new sequence by combining the values item-wise from two sequences. We can consider both as representing collections (in the case of the first primes program described. the resulting sequence will be the same length as the sum of the lengths of the two original sequences. combinations and variations on these ideas are possible and are explored further in the exercises.\ ~\. the resulting sequence will be the same length as the shorter of the two original sequences. Generators 85 subclasses of Collection will. we can consider ways of combining the sequences from two generators to form a generator for a new sequence. We would like to be able to do something like the following: primePlusOne ~ (Primes new) collect: [:x I x primePlusOne first + 1] 3 primePlusOne next 4 In order to do this. For the purposes of exposition we will divide the operators into two categories: selection operators. Gener- . In addition. such as the prime number generator. Consider the problem of producing the sequence of values that are one greater than the prime numbers. or whether it produces values on demand. finally coercing the new collection. Of course. Just as collect: select: and reject: operate on collections to produce new collections. which return some subset of the values of the original underlying generator. the resulting sequence will have a length equal to the product of the two original lengths. which modify the values of the underlying generator. This list is not intended to be comprehensive but merely to illustrate some of the possibilities. Clearly this will not work for infinite generators. Unfortunately. even an infinite collection) of values. such as instances of Interval and the generators described earlier in this chapter. There are three paradigms we will consider. it is necessary that the response to messages such as collect: and select': is to construct a new generator which will.. we would like some method for operating on existing generators to produce new generators. but will not produce any values until demanded. into being one of the appropriate class. if necessary. such as collect:.

assume also that the class Interval has been redefined so as to make it a subclass of Generator. they must respond to first and next. One general way of providing a wide degree of flexibility in behavior is to have the actions of AbstractGenerator be given by a pair of blocks. as we shall see. Because instances of AbstractGenerator are generators. If the value is nil. extremely flexible. Consider first the problem of transforming the values returned by a given generator. We will make Generator a subclass of Collection.5). return nil. nextBlock ~ blockTwo first j firstBlock value next j nextBlock value . j AbstractGenerator new. Their behavior in response to these messages. firstBlock: [testBlock value: self first] nextBlock: [testBlock value: self next] Figure 8." This can be rendered into Smalltalk almost directly: collect: collectBlock I testBlock I testBlock ~ [:x I (x notNil) ifTrue: [ collectBlock value: x ] ]. In response to each of these messages we will produce an object. which must necessarily be of some class. otherwise. Let us call the class AbstractGenerator. but. To simplify the production of examples in this section. however.5 0 The cfass AbstractGenerator I firstBlock nextBlock I [ Class AbstractGenerator: Generator firstBlock: blockOne nextBlock: blockTwo firstBlock ~ blockOne. Like Collection~ instances of Generator will not by themselves be useful. However. A prose description of the operation of collect: might be HProduce a value from the underlying generator.86 The Language ator. will vary in different circumstances. when classes such as the Primes class of an earlier section are made into subclasses of Generator. the full functionality of generators can be obtained through inheritance. Thus the class and methods for AbstractGenerator can be simple (Figure 8. return the value transformed as indicated by the argument block.

thus the solitary variable result in testBlock serves to indicate the value to be returned by the block. we can generate the intersection of two sequences: d ~ a select: [:x I b includes: x d print AbstractGenerator ( 3 5 7 9 ) I . with the ability to take parameters (the argument list) and the ability to be invoked from several locations. \ . t AbstractGemerator new. result I. as in the case of collect:.. and some are explored in more detail in the exercises at the end of this chapter.. firstBlock: [testBlock value: self first I nextBlock: [ testBlock value: self next I Using reject:. the block is in many ways similar to an in-line procedure. In general terms. the "temporary" variable testBlock is not released when the method terminates but must exist for as long as the abstract generator created in the method exists.~ Generators 87 The temporary variable testBlock is in this case used to factor out the commonality between the actions taken in response to first and those taken in response to next. we present the method for reject:. Used in this manner. the method for reject: must loop over values produced by the underlying generator until either the base generator becomes exhausted or until a value failing to satisfy the rejection criteria is encountered.". To illustrate the creation of a generator that produces a subset of the elements from the underlying generator. Notice that because blocks must be evaluated in the context in which they are defined. reject: selectBlock I testBlock result I II testBlock ~ [:x I result ~ x. a block is used to factor out the common behavior in the two messages. Note that the value of a block is always the value of the last expression . we can form the set of values from one sequence that are not elements in a second sequence as follows: ~ 1 to: 10 b ~ 3 to: 15 by: 2 c ~ a reject: [:x I b includes: x c print AbstractGenerator ( 1 2 468 10) a I Using the companion message. [ (result notNiI) and: [ (select Block value: result) whileTrue: [ result ~ self next I. "\ ~. Again. select:.computed in the block. The protocol for other methods is similar.

a shuffle placing values into numerical order could be written as follows: 1 to: 9 by: 4 2 to: 6 by: 2 a shuffle: [:x :y I x > y] with: b AbstractGenerator (1 2 4 5 6 9 ) ~ a b ~ A perfect shuffle can be formed by using a counter. which takes a pair of values. The simplest form is the dot product. a ~ 1 to: 9 by: 4 b ~ 2 to: 8 by: 2 a dot: [:x :y I x max: y ] with: b AbstractGenerator ( 2 5 9 ) The problem of producing a shuffle is considerably more difficult. and produces a new value depending upon this pair. The method is very similar to that used for collect:. the value returned is that of the first generator. 1 AbstractGenerator new. one from each generator. For example. firstBlock: [testBlock value: self first value: secondGenerator first] nextBlock: [testBlock value: self next value: secondGenerator next] For example the dot product can be used to produce a generator which represents the maximum values of two sequences. is evaluated using the shuffle block. .\ 88 The Language Now consider the problem of combining two generated sequences to form a new generator. one from each of the generators. which is then advanced. although intuitively the concept is simple. When either generator becomes exhausted. The new generator halts when one or both of the underlying generators becomes exhausted. If the result of the shuffle block is true the value returned is that of the second generator. dot: buildBlock with: secondGenerator I y testBlock I testBlock ~ [:x :y I «x notNil) and: [y notNil ]) ifTrue: [ buildBlock value: x value: y ] ]. If the block returns a false value. the remaining values from the other generator are returned. Consider a message of the following form: first-generator shuffle: [:x :y I some expression] with: second-generator At each step a pair of values.

'.

;\

\~

\

\

".

Generators

89

a
b

~

~

1 to: 3 10 to: 12

i~O.

a shuffle: [:x :y I (i ~ i + 1) " " 2 AbstractGenerator (1 10 2 11 3 12 )

= 0] with: b

Placing a constant in the shuffle block will result in a catenation.
a shuffle: [:x :y I true] with: b AbstractGenerator ( 10 11 12 1 2 3 ) a shuffle: [:x :y I false] with: b AbstractGenerator ( 1 2 3 10 11 12)

Since at each step a comparison must be made between the next values from each of the underlying generators, it is clear that the method for shuffle must buffer at least one value from each. Let the variables nextx and nexty be the next values from the first and second generators, respectively; then the major portion of the shuffle algorithm can be given as follows:
if nextx is nil then if nexty is nil then return nil else return nexty and advance the second generator else if nexty is not nil and the comparison is false then retu rn nexty and advance the second generator else return nextx and advance the first generator

Adding the code to initialize the buffer variables, this is rendered in Smalltalk as follows:
shuffle: shuffleBlock with: secondGenerator testBlock
~

I nextx nexty result testBlockl

t

[(nextx isNil) ifTrue: [(nexty isNil) ifTrue: [ result ~ nil ] ifFalse: [ result ~ nexty. nexty ~ secondGenerator next] ] ifFalse: [«nexty notNiI) and: [ shuffleBlock value: nextx value: nexty] ) ifTrue: [ result ~ nexty. nexty ~ secondGenerator next] ifFalse: [ result ~ nextx. nextx ~ self next] ]. result ]. AbstractGenerator new; fi rstBlock: [ nextx ~ self fi rst. nexty ~ secondGenerator fi rst. testBlock value] nextBlock: testBlock

\

,

"\

\

"\
"

.'

90

The Language

The catenate form of shuffle occurs often enough to deserve a method of its own.
, secondGenerator t self shuffle: [:x :y I false] with: secondGenerator

We earlier saw how to produce a partial9ifference of two sequences. By combining this with a catenation, we can form a nonrepeating union of two generators.

a
b

~

c ~ a , ( a reject: [:x I b includes: x ]) c print AbstractGenerator ( 1 2 3 4 5 6 7 8 9 10 11 13 15 )

~

1 to: 10 3 to: 15 by: 2

Preliminary to a method for forming the cross-product of two generators, consider a slightly simplier problem: using each value from an underlying generator as a base, construct a new intermediate generator; the desired sequence is formed by the catenation of the values produced by the intermediate generators. Here is an example:
~ 10 to: 30 by: 10 b ~ a atEachGenerate: [:x I (x + 1) to: (x + 3) ] b print AbstractGenerator ( 11 12 13 21 22 23 31 32 33 )

a

The generator a by itself produces the three-element sequence 10 20 30. Each of these values is passed in turn to the argument block, which responds with a new three element generator. By catenating together the values from each of these generators, a resulting nine element sequence is produced. The method for atEachGenerate: incorporates by far the most general and flexible use of blocks shown in this chapter and illustrates once more the procedure-like nature of blocks. Two rec4fsive blocks are used to construct each new value. The block buildGen takes a value from the underlying sequence and constructs an intermediate generator from it, returning the first value from this generator. The block testBlock examines a value from an intermediate generator and either returns it (if it is not nil) or produces the next intermediate generator. Note the difference between the block used in the AbstractGenerator to construct the first element and that used to construct each successive element.
atEachGenerate: genBlock I generator buildGen testBlock buildGen ~ [:x I (x notNil) ifTrue: [ generator ~ genBlock value: x. testBlock value: generator first] ].

I

\
>

).
.
~

\

/~

~~

Generators

91

testBlock ~ [ :x I (x notNil) ifTrue: [x ] ifFalse: [ buildGen value: self next] ].

i

AbstractGenerator new; firstBlock: [ buildGen value: self first] nextBlock: [testBlock value: generator next]

Once the method for atEachGenerate: is defined, it is a simple matter to combine this with a collect: to produce the cross product.
cross: crossBlock with: secondGenerator

i

self atEachGenerate: [:x I secondGenerate collect: [:y I crossBlock value: x value: y ] ]

The following illustrates the type of sequence produced by the method for cross:with:.
~ 1 to: 4 b ~ 10 to: 12 a cross: [:x :y I x @ y ] with: b AbstractGenerator ( 1@10 1@11 1@12 2@10 2@11 2@12 3@10 3@11 3@12 4@10 4@11 4@12 )

a

Further Reading
In the basic form of an expression that can be repeatedly activated to provide a succession of different values, the concept of generators appears in a number of different languages, notably Alphard (Shaw 81), CLU (Liskov 81), MLISP (Smith 80), and Icon (Griswold 83). In most of these languages, however, the use of generators is very restricted. For example, in Alphard and CLUJ generators can be used to iterate over the elements of a programmer-defined data structure but are accessible only in the context of a particular type of for statement. The use of backtracking and goal directed evaluation in conjunction with generators was introduced by the language Icon, a descendent of SNOBOL4 (Griswold 71) and SL5 (Hanson 78). The language Cg (Budd 82) was an attempt to add Icon style generators to the programming language C. However in Cg, and partially in Icon, generators are still restricted to a specific expression in a specific location. The notion of associating the sequence of values produced by a generator with a named variable or an object, and not with a specific occurrence of an expression, is achieved in Icon by a related type of object called a co-expression. Like generators in Smalltalk, co-expressions are not limited to a single specific expression in a program but carry with

92

The Language

them memory of which values have been already produced and can return portions of their sequence in different places in a program. It is, of course, possible to view generators both as objects producing a succession of values and as an embodiment of an actual (perhaps even infinite) sequence. The experimental language Seque (Griswold 85) is an attempt to deal directly with sequences as abstract mathematical objects rather than as data structures. In the Smalltalk-80 language (Goldberg 83), the concept of streams is in many ways similar to the idea of generators. For the most part, streams use a slightly different interface, namely the pair of messages reset (which initializes the generator but does not return any value) and next (which is used for both the first and all succeeding elements). The message do: is adapted from the streams of (Goldberg 83). An article by Deutsch (Byte 81) discusses in more detail many aspects of generators in the Smalltalk80 system.

EXERCISES

1. Rewrite the primes program from the sectionon filters to use instances of AbstractGenerator in place of FilterFactor. 2. Consider the three variables formed in the following manner:

a

<E- 1 to: 3 b <E- AbstractGenerator new; firstBlock: [ a ] nextBlock: [ nil] c <E- AbstractGenerator new; firstBlock: [ a first] nextBlock: [ a next]

Discuss the similarities and differences among a, band c. In particular how does each of them react to the messages first, next and do:? , 3. Look at the method inherited for select: and reject: in class Collection. Explain why it is necessary to implement only one of these messages in class Generator. 4. An alternative to the first next paradigm described in this chapter is the following pair of messages:
reset next

Reset the generator to the start of the sequence, but do not produce a value. Return the next value from the sequence (which may be the first value if the last message sent to the receiver was reset).

Similarly, instead of returning nil to indicate the end of sequence, a message atEnd could be implemented by each generator. This message would return true if no more elements could be returned by the generator, and false otherwise. Discuss the advantages and disadvantages

\

.".:}

Generators

93

of these techniques. Support your opinions by rewriting some of the generators described in this chapter so that they use these alternatives. 5. Explain why the following does not produce the expected result:
a ~ 1 to 9 b ~ a shuffle: [:x :y I true] with: a

Is this a problem inherent with the generators technique described in this chapter? How might it be overcome? 6. Why does the following not succeed in producing a sequence of length 4?
i ~ (Primes new) select: [:x I x < 10 ]

Show how to implement methods for the following messages:
while:

until:

The argument must be a one-parameter block returning a boolean value. Return values from the underlying generator as long as the block evaluates to true. Terminate when the underlying generator is exhausted or when a value is found for which the block is not true. Like while: The argument must be one-parameter block returning a boolean value. Produce values from the underlying generator until the block returns true, or until the generator is exhausted.

Is it necessary to implement both of these in terms of AbstractGenerator? Show how, if a method exists for either one, the other can be implemented in terms of it. 7. The following method in class Generator produces a sequence consisting of the first n elements of the underlying generator, where n is the integer argument.
first: limit

I counter testBlock I

testBlock ~ [:x I counter ~ counter + 1. (counter> limit) ifFalse: [ x ] ]. t AbstractGenerator new; firstBlock: [ counter ~ O. testBlock value: self first] nextBlock: [testBlock value: self next]

Show how to implement a method for the message from:to:, that can be used to extract any contiguous set of values from the underlying generator, specified in terms of indices. Extend this to return any set of values for which the indices form an arithmetic progression. 8. Let us say a generated sequence is enumerable if every value in the

.2) .. c) The underlying generator is infinite.m) (3. Construct a method which produces the same values but in the following dovetailed sequence: (1..1) (1. (1.. Consider the following scheme for assigning indices to the values produced as a result of the message atEachGenerate:. 9.... and some of the intermediate generators are also infinite. (2. however.2) ..2) (2.1) .2).1) (1. all of the intermediate generators are finite.2) (3.. Under which of the conditions given in problem 6 is the resulting sequence enumerable? . b) The underlying generator is finite.1) (1. (I. (2.4) (2. then the sequence of indices produced can be represented as follows: (1.1). Note that this involves keeping a list of intermediate generators. rather than just the single intermediate generator required for atEachGenerate:. some of the intermediate generators are infinite. Let (IJ 1) be the index of the first value produced by the first intermediate generator. each of the intermediate generators is finite. the index of the second value produced by the first generator. If the first intermediate generator produces n values and the second intermediate generator m values. assuming the generator is queried long enough. and so on. however. under which of the following conditions is the resulting sequence enumerable? a) The underlying generator is finite.Consider the message atEachGenerate:.1) (2.1) .2) (4.n) (2. d) The underlying generator is infinite. the first value produced by the second generator.3) (2.94 The Language sequence will eventually be produced.3) (3.1) (1.

. ~.< < »:>:::. >-.1\ ~~' .::. . .c... * * * * * >1* * * * * * * >} (8) -~S~ .{{{{{{{{{{{{{{{{{{{{{{f{{ '" "'" "'" '.~ :.'.* * '" '.8'I f\ (§) ~ ~ ~~ * * < <: « ~ .~. % f\ f\ l\ A ~} :8) (21 (8:~ (~) !0 ~~.~j :. ..»»:» ////////// CHAPTER {{{{{{{{{{{{{{{{{{{{{{{{{i -}}}}}}}}}}}}} }}}}}}}}}}}} ««{«««({«««««i 9 Graphics »»»»» //// / / / / / / '.f:::~ - /\ !\ /\ /\ /\ «~ ::ti~ '#: r. 1\ /\ 1\ i- /\ /\ /\ /\ '#: ~~ ~ ~ ~ ~ ~ ~ ~ ~ ~ * * * :<.. .-"'" "'" "'' ' ' '" '" ))))))))))))))))))))))\ ~}}} H}}}}}}}}}}}}}}H}}}} ({({«({«««««««(i 95 ..

(One experiment involved teaching the Smalltalk-72 language to a group of children ranging in ages from six to fifteen. however. a goal of the LRG was to develop a programming environment that would be useful and accessible to nonspecialists. because of its nature. the screen itself was sensitive to the user's touch. As a first step toward the DYllabook. One of the first motives in developing the Little Smalltalk system was a desire to provide for a large number of people who might otherwise not be exposed to Smalltalk. but. In the Smalltalk-80 system. The following quote is notable: . the bit-mapped display and the mouse are represented as an intrinsic part of the programming environment. In the early 1970s. To achieve this. were brought strongly into focus when children down to the age of six were seriously considered as users. We also realized that children require more computer power than an adult is willing to settle for in a time-sharing system. 1. particularly those having to do with expressive communication. In its original idealization. Unfortunately. "After observing this project we came to realize that many of the problems involved in the design of the personal computer.96 The Language The Smalltalk language was originally conceived as part of an ambitious project to design a Dynabook.by Alan Kay. a slightly more accessible one. bit-mapped display and a pointing device that could be used to reference and manipulate images on the screen. particularly children. The Dynabook project grew out of ideas developed in the late 1960s. and they usually find the things that can be accomplished with a low-capacity time-sharing system insufficiently stimulating to maintain their interest. Children." . 1) That project eventually developed the Smalltalk-80 programming environment. Kay went to the Xerox Palo Alto Research Center (Xerox PARC) and there formed the Learning Research Group. Thus the Little Smalltalk project represented a much less revolutionary and ambitious project than the Smalltalk-80 programming environment. The best outputs that timesharing can provide are crude green-tinted line drawings and square-wave musical tones. and thus a portion of the screen could double as a keyboard. high resolution. Actual ftinterim Dynabooks" developed at PARC kept the bit-mapped display but replaced the touch screen with a separate keyboard and a pointing device called a mouse. it was necessary to design a system that could execute under a conventional operating system on conventional processors using nothing more than conventional character-oriented terminals. A premise of Kay's work was that eventually it would be possible to place a computer with power equal to machines that occupied entire rooms into a portable box about the size of a notebook. See "Microelectronics and the Personal Computer" (Kay 77) for a description of this project. The Dynabook was conceived as incorporating a page-sized. color television and sterophonic records. for many computer users. access to high resolution bitmapped displays still represents an unfilled aspiration rather than an everyday experience. are used to finger paints.

which can display straight lines. For this reason this chapter is divided into three parts. The curses terminal output package consults this database. Unfortunately. line graphics.. The final part. C. and a few will be able to experiment with the third form of graphics. as with almost all attempts at device-independent graphics. (See the section on windowing in the Projects chapter. and other line forms. finding the entry which matches the terminal the user is running on. develops techniques that can be used with devices which permit the user to set the value of individual pixels (dots). One attempt to circumvent this problem is the termlib/curses facility. This document is usually distributed along with the Berkeley 4. It is assumed that most users will have access to devices for which the first forms are possible. Each terminal manufacturer seems to use a different protocol for screen operations. it is still possible to produce some graphics functions using Little Smalltalk. such as the ability to do windowing. or moving the cursor to a specific location and printing a string. ~ '. The first part describes routines for simple character graphics.2 The name termlib is used to describe a database of terminal descriptions.2 (and subsequent) version of Unix. inserting characters.. R. 2. bit-mapped graphics. From the description given there. The interested reader may wish to investigate how these can be incorporated into the Little Smalltalk system. printing a string.. even such a simple operation as this is thwarted by the great number and diversity of terminals in existence.\. Arnold. many will find the second level accessible. Character graphics require nothing more than conventional terminals but are therefore severely constrained in the nature and quality of the results.) . Character Graphics The primitive operation upon which all our character graphics will be constructed is moving the cursor to a specific location on the screen (usually expressed by a Point representing an x-y coordinate pair) and at that location. ~\ Graphics 97 Despite the lack of emphasis on graphics in the Little Smalltalk system. discribes routines for devices such as the Tektronix 4014 terminal.} \. The second part of this chapter. such as clearing the screen. ':. The type of graphics capabilities attainable depends on the nature of the devices on which the Little Smalltalk system is executed. The curses screen package is described in "Screen Updating and Cursor Movement Optimization: A Library Package" by Kenneth C. backspacing. it determines the correct sequence of operations necessary to perform simple terminal output. circles." The curses package actually provides many more capabilities than are used here.0: \ . It is also available under many versions of Unix that advertise "Berkeley enhancements.

Internally. arid in particular. form. which displays as follows: 1"- "- 1 -. A form represents some printable image. instances of the class CharacterFonn.maximum size of any string the width. You can discover whether your system has been configured to use the package by sending tpe message printAt: to a string. 1 • would produce a form representing an airplane. For example. the class C4aracterFonn maintains an array of strings. The primitive units with which character graphics are defined are forms. row: 1 put: I row: 2 put: I row: 3 put: 1 1"row: 4 put: I 1"1/ row: 5 put: I [-. for example 'hello world I printAt: 10 @ 10 If the result is printed at the specified location. combination operations which combine one form with another. We can categorize the operations we will provide for forms into four groups: definition operations which describe the basic nature of the form.1). but related.* /1 0 row: 6 put: I <--------/ 1-----1 1• row: 7 put: 1 "- II. the following sequence: plane plane plane plane plane plane plane plane <E- CharacterForm new. representing the textfor the form (Figure 9. so as to distinguish them from the forms we will discuss in subsequent sections.* 11 0 <--------1 /-----[ ["-"-'--------1 ---------/---1 .98 The Language The primitive operations necessary to support character graphics are hnplemented using calls on the curses output package. transformation operations which metamorphose the form into a different. Elements in the text array are defined row by row. using the message row:put:. The size of this array represents the height of the form and the . and display operations which print a form on the output. the curses routines are operational on your system.

By using the fact that these spaces will overprint earlier versions. starting in the upper left hand comer of the screen: (1 to: 9) do: [ :i I plane printAt: i @ (3 * i) ] Reversals. text at: index put: aString rows t text size Because of the ability to print a string at a specific location on the teIminal. rotations and clipping are among the transfoImations to . we can use this example to produce a simple type of animation. instances of CharacterForm can also be displayed at a specific location using the following method: printAt: aPoint I location I location ~ aPoint copy. For example the following will cause the plane to move to the right and down.Graphics 99 Figure 9. text do: [ :x I x printAt: location. location x: (location x + 1)] Note the spaces surrounding the characters in our plane example.1 0 The class Character Form Class CharacterForm I text I [ new text ~ Array new: 0 columns t text inject: 0 into: [ :x :y I x max: y size] extent t self rows @ self columns row: index t text at: index ifAbsent: [ I I ] row: index put: aString (index> text size) ifTrue: [ [ text size < index] whileTrue: [text ~ text grow: 1 I ] ].

(I to: rows) do: [ :j 1 newRow at: «rows . identified by a pair of points representing the upper left-han"d and lower right-hand corners of the clipping. A reversal is merely a mirror image of a form and can be constructed using the following method: reversed r newForm columns NewRow columns ~ self columns.self rows. i newForm A clipping represents a square subportion of a form. i newForm 1 Note that the original form can be Uragged. newForm row: i put: newRow]. To insure that columns line up correctly in the reversal each row must be padded with spaces to a uniform length. (1 to: self rows) do: [ :i 1 newRow ~ (text at: i) padTo: columns. since an image with an equal number of columns and rows does not display as square." that is. the clipping from 2 @ 2 to 5 @ 9 of our plane example produces the following: . For example.j) + 1) put: «text at: j) at: i ifAbsent: [ $ ] ) ]. 100 The Language modify a form. (I to: self columns) do: [:i 1 newRow ~ String new: rows. only the strings.\ ."'-I o II *-1 -------- "'- 1-----/ 1-------. the rows need not all be of the same width."'.'. newForm ~ CharacterForm new. For example. newForm row: i put: newRow reversed ]. Nevertheless.0:. our plane becomes the following when reversed: "'-I 1----1 1------. Unfortunately. a rotation can be accomplished using the following method: rotated 1 newForm rows newRow 1 rows oE. and not the individual characters are reversed. newForm ~ CharacterForm new. This can cause odd effects if a great number of asymmetric characters are used.< Rotations have a similar problem.

rowSize ~ sourceRow size. sourceForm do: [ :sourceRow 1 rowText ~ self row: newRowNumber.I. newRowNumber ~ newRowNumber + I] . (I to: rowSize) do: [ :i 1 «sourceRowat: i) ~ = $ ) ifTrue : [ rowText at: (left + i) put: (sourceRow at: i) ] ]. A transparent overlay of one form on another can be accomplished using the following method. rsize ~ 10werRight y .Graphics 101 1""1"". allowing whatever was located in the original to show through. (upperleft x to: 10werRight x) do: [ :i 1 newRow ~ String new: rsize. rText ~ self row: i. An opaque overlay completely obliterates whatever was originally found in the area where the form is being placed. left ~ startingPoint y .left. II left hand sidell top ~ upperleft x .I. newForm row: (i .I. i newForm A form can be placed into another form in one of two ways. newForm ~ CharacterForm new. overLayForm: sourceForm at: startingPoint 1 newRowNumber rowText left rowSize 1 newRowNumber ~ startingPoint x..* Clippings can be produced using the following method: c1ipFrom: upperleft to: 10werRight 1 newForm newRow rsize left top rText 1 left ~ upperleft y . rowText ~ rowText padTo: (left + rowSize). self row: newRowNumber put: rowText. The more useful transparent overlay places only the nonspace characters.""-. (I to: rsize) do: [ :j 1 newRow at: j put: {rText at: (left + j) ifAbsent: [$ ] ) ].- ""- 1-.top) put: newRow].

The exercises at the end of this chapter suggest several other uses for character graphics. The interface to the class Pen is shown in Figure 9. such as the Tektronix 4014. which in turn was inspired by "Turtle Geometry" in the language LOGO (Abelson 81). For example. may have a C interface permitting terminal manipulation that is different from the plot(3) interface. For this reason there may be a different version of sf created for each device supporting the plot(3) functions. or down. 4 Conceptually. placing the cloud at the appropriate location and then transparently laying the plane over the cloud. the plot routines use a different C library for each terminal type. As each frame is constructed it is printed and work is begun on the next frame. pens can be used to draw various shapes. in which case the pen produces a line as it is dragged across the terminal screen. A Unix interface has been provided to these. one our plane and the second a cloud. describing the current location of the pen. Using the message exte11t:to:. In line graphics the fundamental object is the Pen. Also note that some other terminal types. The third quantity is a direction. but the cloud will still appear through any blank spaces in the plane form. 3 In character graphics the fundamental unit was the CharacterForm. The first two are a pair of coordinates. Four quantities describe the state of the Pen.) 4. To use these terminals may require modifying the primitive handler. (See Chapters 11-15. expressed as a value between 0 (straight up) and 27T (also straight up). Thus the terminal type is compiled as part of the executable file. in which case pen motions do not produce any output. such as straight lines or circles. which determine the terminal type at run time. Line Graphics Some terminals. the plane will appear to be in front of the cloud. Suppose.2. With the transparent overlay. The final quantity is a binary state which is either up. the user can control the range of legal values for pen coordinates. for example we have defined a pair of forms. The class Pen is adapted from the Smalltalk-80 graphics kernel. a Pen can be thought of as a writing instrument for the terminal screen. Bya sequence of simple directives. can draw a limited range of figures. the following expression: 3. such as the HP 2048. Note that unlike the curses routines.102 The Language Slightly better "animation" can be produced using transparent overlay. through the plot(3) routines. . Each flframe" of the animation can be displayed by creating a new form.

Move to the location given by the argument. turn: 0. Turn the direction of the pen clockwise by the amount given in the argument. The arguments represent the lower left-hand ~nd upper right-hand corner~. Erase the terminal screen. 4 timesRepeat: [ aPen go: 10 . When up. Return true if the pen is curre!1t1y up.\.2 D Interface to the classPen message ci rcieRad ius: direction direction: down erase extent:to: go: goTo: isUp location turn: up function Draw a circle cen~ered at the current location with a radius given by the qrgument. Move in the current direction by an amount given in the argument.5 pi ] draws a box: D We can try this with various numbers of sides to draw a sequence of regular polygons: - .\ Graphics 103 Figure 9. respectively. w~ich must be a Point. Return the current direction of the pen (a number between 0 and 21T). Establish the coordinate range for the pen. movement will not cause output. Set the pen st~te to down (movement will cause output). Set the state of th~ pen to up. false otherwise. Set the pen to point in the direction given by the argument. Re~urn the cyrrent location of the pen as a Point.

aPen goTo: startPoint. it is necessary to store the information about a given image. endPoint <E. turn: ( 2 pi Insides) ] ] Producing the following picture: A pen permits one to draw directly on a screen. Figure 9. In order to preserve and manipulate images. from: (startPoint + offset) to: (endPoint + offset) drawWith: APen aPen up. In addition.104 The Language (3 to: 6) do: [ :nsides Insides timesRepeat: [ aPen go: 10 .ePoint + offset t Line new. a new Line can be created offset from the original. Instances of class Line (Figure 9.3 0 The class Line Class Line I startPoint endPoint I [ from: sPoint to: ePoint startPoint <E. aPen goTo: endPoint . aPen down.3) maintain a starting and ending point and can be instructed to display themselves using a specified pen. A first step is to define a class representing an individual line.sPoint.

but. as with the character case. rotations by an arbitrary amount. Note carefully the use of self and super to insure the messages are matched to the correct methods. Using stencils and forms. can be instructed to.5). however.Stencil new.4 D The Class LineForm Class LineForm I lines I [ new lines <Eo. Also. The class LineForm is useful by itself if the starting and ending points are easy to compute.aPen turn: (2 pi / nsides ) ] ] penTwo <Eo. it would be inconvenient to rewrite the computation merely to save the endpoints of the lines generated. New lines are added to the collection by means of the message add:to:.Bag new add: startingPoint to: endingPoint lines add: ( Line new. or clipping) and combinations of line forms can 'be constructed.4) consists of a collection of Lines. methods to produce various transformations (such as reversals. called Stencil (Figure 9.LineForm new aPen <Eo. it saves the lines it would have generated in a form. we could make three copies of our polygon picture as follows: aForm <Eo. Because of inheritance.\ Graphics 105 Figure 9.Pen new aForm with: penTwo displayAt: 0 @ 0 aForm with: penTwo displayAt: 10 @ 10 aForm with: penTwo displayAt: 0 @ 20 Producing the following picture: . setForm: aForm (3 to: 6) do: [ :nsides Insides timesRepeat: [ go: 10 . from: startingPoint to: endingPoint ) with: aPen displayAt: location lines do: [ :aLine I (aline + location) drawWith: aPen 1 A LineForm (Figure 9. Like CharacterForms. display themselves at a specific location on the terminal screen. it is necessary only to redefine the single message goTo:. A better solution is to use a subclass of Pen. If a figure is produced as part of a computation using a Pen. A Stencil is like a Pen. instead of actually writing on the terminal screen. instances of LineForm.

5 0 The Class Stencil Class Stencil: Pen 1 saveForm I [ setForm: aForm saveForm ~ aForm goTo: APoi nt (self isUp) ifTrue: [ super goTo: aPoint ] ifFalse: [ saveForm add: self location to: aPoint. A device that can do this is often called a "Bit-Mapped Display.\ )." Unfortunately. - i 06 The Language Figure 9. super goTo: aPoint self down] I Bit-Mapped Graphics The most general type of graphics requires the ability to turn on and off individual pixels (dots) on the terminal screen. self up. such devices are .

fy) / (tx . fy) to (tx. if we group bits by eight and read from top to bottom. The Pen described here is extremely crude. For example. this form can be represented in a number of ways. so we wish to draw a line from pixel (fx. 5 The only message which would require changing to support pens is the drawing produced by the message goTo:. can be constructed out of bit operations. Internally. Assume furthermore that fx is to the left of tx. Assume the endpoints of a line have been already normalized. The following algorithm then draws a rough line one pixel wide. Bit-mapped graphics are very powerful since they can be used to represent anything that can be displayed on the screen.fx). J A two-Dimensional form possesses a height (7 in this case) and a width (19). I yf ~ fy asF loat.6 illustrates a digital form representing the word "Budd. left to right). Primitive operations would have to be defined to turn on and off a specified set of pixels on the terminal from this representation. . the class Pen. For example. the height and width could be maintained by a Point ( 7 @ 19) and the bit values by a ByteArray ( #[ 224 4 50 0 134 64 16 242 94 121 74 120 231 56 ]. For a more detailed description of a better pen algorithm.6 0 A digital form representing th~ word Budd • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • • still relatively new and their interface completely unstandardized. described in the last section. Figure 9. Therefore the concepts of bit-mapped graphics can be discussed only in rough general terms. see the description of the graphics kernel (Goldberg 83). representing either dots or spaces. a digital (bit-map) form can be thought of as a two-dimensional array of bits. ty). for example. ydiff ~ (ty . Abstractly.Graphics 107 Figure 9. (fx to: tx)do: [ :x I set point at x @ (yf rounded) yf ~ yf + ydiff] 5. leaving the specific details for the user to work out. We here describe only a portion of the algorithm necessary to support this command.

~ ':. it may be possible to achieve quite complex effects. Unlike the Smalltalk-80 system. however. 6. As an indication of the complexity these operations can involve. such as shading and halftoning. > ~\ \ . instances of BitBIt are initialized with the eight-argument message destForm:sourceForm:halftoneForm:combinationRule: distOrigin: sourceOrigin:extent:elip Rect: . these effects are only incidental to Little Smalltalk. which is at the heart of the Smalltalk-80 graphics kernel. an excellent description of the techniques used in the Smalltalk-80 graphics kernel can be found (Goldberg 83 6 ). The Smalltalk-80 book gives a detailed description of the infamous BitBIt (for bit block transfer) operation. so we will not discuss the possibilities here..\ \~\ 108 The Language Depending upon the quality of the output device being used.

f{{{{{{{{{{{{{{{{{{{{{{{{ '" '" '" '" '" '" n}}}}} D}}}}}}}}}}}}}}}}} ({«««((((({((! i)))))))))))))))))))))) 109 . " .'" '" " -}}}}}}}}}}}}}}}}}}}}}}}}l ««««{««««««{(. {{{{{{{{{{{{{{{{{{{{{{{{{.1 '" '-" '" '" '. 10 Processes / / /// / / / / / / "- '-'" '-" -'-.: ~.... . /////////// CHAPTER '" "-. .\ '\ >.

the generator can abstractly be considered to be producing some commodity.1 0 The producer-consumer relationship. An alternative organization is shown in Figure 10. These two viewpoints are reflected in the two protocols described in Chapter 8 for dealing with generators. consumer controlling Producer Consumer consumer requires a value producer next consumer is suspended i producer produces a value value consumer resumes execution . there are basically two organizing schemes one could use: either the generator process is considered to be "in control" and it calls the consumer process for processing each element. That is. Between these uses. They both have their advantages. . Which of these two viewpoints is the "correct" way to view the producer-consumer relationship will depend upon circumstances. once control is given to the producer via the message do:. a third alternative is preferable. it initiates the producer using first.1). calling the generator for each new element produced. following whatever control flow is necessary. no execution takes place in the producer.. Here. In the first scenario (Figure 10. they can run Figure 10. and the calling routine considered to be a consumer processing instances of the commodity. Each time a subsequent value is requested. Notice that the consumer can follow any desired control flow. the producer is queried using next. however. Between calls to next. can be viewed in terms of a producer-consumer relationship. introduced in chapter 8.'. no execution takes place in the consumer.2. the consumer is !lin control.. the producer is "in control.a message. Rather than one process subordinate to the other. but that the response of the producer is given by the same code and is executed repeatedly in response to first and next." When the consumer requires a value. the block used as argument with the do: command is executed using the value as argument. '\ 110 The Language The notion of generator. or the consumer process is considered to be the dominant force. In some instances.. As each element in the generated sequence is produced.\ "\'> .." The producer is free to interpret this message in whatever manner it desires. In a system where either the producer or consumer must directly request action from the other via.

Of course. two or more processes must be permitted to execute in paralIeI. the results will be correct. The term here refers to logical.3). on a single processor machine very little ever actually happens physically in parallel. . 2. the values are placed intb a common container called a mailbox or port.4. Because of the symmetry of this relationship.2 0 The producer-consumer relationship. As the producer generates each value.2 In Little Smalltalk the user can create a new process using the message newProcess and a block. If the coroutine technique is to be allowed in Smalltalk. As long as: the sequence of deposits and retrievals meshes correctly. Note that in this scheme neIther process initiates the other. such processes are called coroutines. A class description for a sirriple form of mailbox is shown in Figure 10. at least at the program level. parallelism. not physical. I Each time the consumer requires a value~ it retrieves the object most recently placed into the mailbox. producer controlling Producer producer started using do: Consumer computes a hew value consumerBlock value: newValue (producer is suspended) consumer is started consumer executes block consumer returns producer resumes execution in parallel (Figure 10. Peter Deutsch (Byte 81).Processes 111 Figure 10. and each can execute independently. aProcess ~ [ some actions] newProcess 1. The term port in this context and many of the ideas used in this chapter are due to L.

4 0 A simple version of class Mal180x I holder I [ Class MailBox place: anltem holder ~ anltem retrieve t holder . Figure 10. A suspended process can be made active by passing it the message resume.\ \. There are four possible states in which a process can be. A suspended process is one that is ready for execution but is not currently executing. An active proc-ess can be temporarily halted by passing it the message suspend.3 0 The symmetric producer/consumer relationship Producer producer produces first element places element in mailbox producer starts producing second element places second element in mailbox starts producing third element Consumer consumer picks first element from mailbox consumer processes first element consumer picks second element from mailbox A newly created process does not immediately begin execution. They are described as follows: Active Suspended An active process is one that is currently executing. It can be permanently halted by passing it the message terminate. it is said to be in a suspended state. Instead. 112 The Language Figure 10.

A newly created process can be directed to commence execution by passing it the message resume. and output from two separate processes may intermix in arbitrary ways. Blocked processes are unblocked by the associated semaphore. The message fork is a combination of newProcess and resume and can be used to create unnamed processes. \ . The message state will return a symbol indicating the current state of a process. A process can commit suicide by passing the message terminate to selfProcess. The process sending the message will be restarted again when it reaches the front of the queue. The message yield is a no-op (it returns nil) but has the side effect of passing control to the next process in the queue of active processes. A terminated process is one that has either finished normally or been terminated explicitly by a terminate message. Executing processes run in parallel with each other. For example: [ 5 timesRepeat: [ Iprocess one· print] ] fork 5 timesRepeat: [ ·process two· print] process one process two process two process one process one process one process two process one process two process two These are also versions of fork: and newProcess: which permit parameters to be passed to arguments in blocks. although the object representing it will continue to exist as long as there are pointers to it. A terminated process cannot be restarted. aProcess resume The pseudo variable selfProcess always refers to the currently running process. \ ~\ \): Processes 113 Blocked Terminated A blocked process is one that is currently on some semaphore queue (described below).\. [ :num I num timesRepeat: [ Jdid it ' print] ] forkWith: #( 3 ) did it did it did it .

\ 5~ " . Little Smalltalk defines a new class of objects call Semaphore. counter ~ Semaphore new: 0 place: anltem items addLast: anltem. and then overridden without being read. In order to synchronize the actions of two or more executing processes. A sema- Figure 10. t items removeFi rst . Furthermore. retrieve counter wait. Here input 'abc' is placed into the box.'\ •.6 0 An improved mailbox I items counter I [ Class MailBox new items ~ List new.5 0 A problem with the simple mailbox Producer mailbox place: labc l Consumer mailbox place: Ixyzl mailbox retrieve mailbox retrieve Semaphores The fact that we cannot predict ahead of time the way in which two independent processes will interleave illustrates one of the pitfalls of concurrency. Consider the sequence of events shown in Figure 10.5.\ \ 114 The Language Figure 10. input 'xyz' is read twice. counter signal. The first problem is easy to fix by maintaining in the mailbox a list of entries rather than just a single item. The second problem is more difficult.

signal and wait. The first. the results can be unexpectecl.6. In the producer/consumer example. A semaphore can be created with any desired initial value for the counter by passing a number with the message new:. and any other process that requests it will be suspended until the process currently holding the permission relinquishes it. When the consumer attempts to remove an object from an empty mailbox.Processes 115 phore responds to a pair of messages. Using this facility. The critical section problem can be solved using a binary semaphore. all but the first will be suspended. Suppose the producer and the consumer attempt to access the mailbox at the same time. The message signal either increments the counter. is a counter and is used to suspend the consumer if there are no elements in the items . our mailbox example can be improved as shown in Figure 10.7. The class Semaphore provides the following method for implementing critical sections: critical: aBlock self wait. self signal If two or more processes attempt to execute blocks established as critical sections for the same semaphore.6 Monitors There is one problem that is not solved by the use of semaphores in Figure 10. Sections of code for which it is important that only one process be executing at any time are called critical sections. If the counter is zero. Here two semaphores are used. and can abstractly be thought of as implementing a non-negative counter and a queue of pending processes. If the counter is nonzero. A value of one means no process currently has this permission. its value is merely updated. Such a solution is shown in figure 10. or resumes the first process waiting for the semaphore. aBlock value. it will be suspended until the producer has had time to produce an entry. if it already has a nonzero value. and each will be restarted in turn as the earlier processes are finished. and any process that asks for it will be granted. The message wait attempts to decrement the counter. A value of zero means that some process currently is holding the permission. A binary semaphore is simply a semaphore that takes on the values zero and one. Such a semaphore can be thought of as encoding a permission to perform some task. as before. one use for a semaphore is as a counter measuring the number of items placed in the mailbox. the currently running process is suspended. Since both methods may attempt to modify the list containing the mailbox entries simultaneously.

but because it can be considered a model for a large class of concurrency control problems. such as the mailbox example provides for the items list. The dining philosophers is considered a classical problem in synchronization. In the center of the table there is a bowl of rice. Massachusetts: Addison-Wesley.items removeLast ]. a . The philosophers share a common circular table surrounded by five chairs.): "Five philosophers spend their lives thinking and eating. When a philosopher thinks. Silberschatz. mutex +.List new. and the table is laid with five chopsticks (Figure 10. we will examine a solution to the "dining philosophers" problem. The encapsulation of a data structure in an object which manages its own critical section behavior. Dining Philosophers Problem To further illustrate the utility of semaphores.Semaphore new: 1 place: anltem mutex critical: [ items addLast: anltem ]. pp. Operating System Concepts.\ 116 The Language Figure 10. each belonging to one philosopher. not because of any practical importance. L. 1985). he does not interact with his colleagues. The problem can be stated as follows (from J.7 D The mailbox as a monitor Class MailBox I items counter mutex I [ new items +. mutex critica I: [ resu It +. The second semaphore is used to implement a critical section around the insertion or removal of information from the list. t result list. is known as a monitor. counter signal. 347-348. (Reading.8). From time to time. I result I retrieve counter wait. counter +.Semaphore new: O. Peterson and A.

rightChopStick wait. he eats without releasing his chopsticks. II While the use of semaphores guarantees that no two philosophers can . the chopstick is released by passing the signal message to the semaphore.". rightChopStick signal. Similarly. we must ensure that no two adjacent philosophers can pick up the same chopstick at the same time. he puts down both of his chopsticks and starts thinking again. ]. When he is finished eating.\ . When a hungry philosopher has both his chopsticks at the same time. A philosopher endeavors to possess a chopstick by passing the wait message to the associated semaphore. II set down chopsticks leftChopStick signal. A philosopher may only pick up one chopstick at a time. self eat. it does not overcome the second problem associated . This problem is easily solved by representing each chopstick by a (binary) semaphore. eat simultaneously.8 0 The table used by the dining philosophers o D philosopher gets hungry and tries to pick up the two chopsticks that are closest to him (the chopsticks that are between him and his left and right neighbors). Obviously. First. self think. Processes 117 Figure 10. Thus the life of a philosopher can be described as follows: [true] whileTrue: [ " pick up chopsticks" leftChopStick wait.0:. he cannot pick up a chopstick that is already in the hand of a neighbor." The principal difficulties encountered in a solution to this problem are twofold.

Thus if rand is the . We will use the third solution. Each philosopher is assigned a unique number. . otherwise. state) print Similarly the method releaseChopSticks implements the transition from eating to non-eating. That is. I is I. releaseChopSticks self pri ntState: Ifinished I. each holding one chopstick in his left hand waiting patiently for his neighbor to finish eating (although his neighbor is similarly waiting for his neighbor). Several possible remedies to the problem of deadlock can be proposed. Allow a philosopher to pick up his chopsticks only if both of them are available. Thus our philosophers will sit forever. the philosopher picks up his left chopstick first. For example. using the method printState to print out a record of the change of state. we include a random variable. name. rightChopStick signal In order to introduce a bit of nondeterminism into the solution. leftChopStick wait] printState: state ( IPhilosopher I. '} \ \ '. represented by the process yielding control to the next process a random number of iterations. Use an asymmetric solution. leftChopStick signal. an odd philosopher picks up first his left chopstick and then his right chopstick. while an even philosopher picks up his right chopstick and then his left chopstick. We will also use this value to print out a message each time the philosopher changes state (for example going from thinking to eating). Each will grab his left chopstick.' 118 The Language with this exercise. Peterson and Silberschatz list the following: D D D Allow at most four philosophers to be sitting simultaneously at the table (so there is always an empty place). as each philosopher tries to grab his right chopstick he will be delayed.). maintained in the variable name. The methodgetChopSticks describes the selection of both chopsticks. If the number is even. namely that of deadlock. Imagine that each of the five philosophers becomes hungry simultaneously.> . Since no philosopher will release either of his chopsticks until he has eaten. . GetChopSticks self printState: lmoving l • «name " " 2) = = 0) ifTrue: [ leftChopStick wait.. rightChopStick wait] ifFalse: [ rightChopStick wait. Each philosopher eats or thinks for a period of time randomly determined. he picks up his right chopstick first.

self releaseChopSticks ]. we introduce a counter time. A representative output for five philosophers eating two times a day is as follows: Figure 10. name +. This counter represents the number of times a philosopher will eat in a day. The argument to the message new: is the number of philosophers and that of the message dine: is the number of times the philosopher will eat in a day.9. self getChopSticks.rchop. rightChopStick +. self eat.10) can be used to initialize the chopsticks semaphores and the philosophers appropriately.Ichop. s. Thus one day in the life of a philosopher is represented by the following: time timesRepeat: [ self think. Program Continued . the processes of eating and thinking can be represented as follows: eat self printState: leating l .'. rand randomize.9 0 The class Philosopher Class Philosopher I rand leftChopStick rightChopStick name [ ~~w: aNumber rand +. The Class DiningPhilosophers (Figure 10. (rand randlnteger: 15) timesRepeat: [selfProcess yield] think self printState: lthinking '. Processes i19 random variable.aNumber I leftChopStick: !chop rightChopStick: rchop leftChopStick +. self sleep.Random new. Putting everything together gives us the class Philosopher shown in Figure 10. (rand randlnteger: 15) timesRepeat: [selfProcess yield] In order to present a finite solution.

I is I. self printState: 'sleeping! ] fork Philosopher 1 is thinking. leftChopStick wait] printState: state ePhilosopher I. Philosopher 1 is eating. Philosopher 3 is thinking. name. 120 The Language getChopSticks self printState: Imoving ' . state) print releaseChopSticks self printState: 'finished ' . think self printState: 'thinking ' (rand randlnteger: 15) timesRepeat: [selfProcess yield] eat self printState: 'eating ' . self eat. ((name" " 2) = = 0) ifTrue: [ leftchopStick wait.\. self releaseChopSticks ]. (rand randlnteger: 15) timesRepeat: [selfProcess yield] philosophize: time [ time timesRepeat: [ self think. rightChopStick signal. self getChopSticks. Philosopher 3 is eating. rightChopStick wait] ifFalse: [rightChopStick wait. Philosopher 5 is thinking. Philosopher 2 is thinking. Philosopher 4 is thinking. . leftChopStick signal.

philosophers at: p put: (Philosopher new: p) ]. Philosopher 2 is thinking. Philosopher 3 is thinking.\ . \ "\ . Processes 121 Philosopher 5 is eating. Philosopher 4 is eating. Philosopher 2 is eating. Philosopher 4 is eating. (1 to: numberDiners) do: [ :p I (philosophers at: leftChopStick: (chopSticks at:p) rightChopStick: (chopSticks at: «p " " numberDiners) dine: time (1 to: number Diners) do: [ :p I (philosophers at: p) philosophize: time] + 1)] ."""=. Philosopher 5 is eating...>. \ Philosopher 5 is sleeping. chopSticks ~ Array new: numberDiners. Philosopher 1 is thinking. . Philosopher 2 is eating. Philosopher 3 is sleeping.. Philosopher 4 is sleeping. . Philosopher 2 is sleeping. Figure 10.. (1 to: numberDiners) do: [ :p I chopSticks at: p put: (Semaphore new: 1).~. Philosopher 1 is sleeping. Philosopher 5 is thinking.. Philosopher 1 is eating. Philosopher 3 is eating. philosophers ~ Array new: numberDiners. Philosopher 4 is thinking. ..10 0 The class DiningPhilosophers I numberDiners chopSticks philosophers I [ Class DiningPhi losophers new: aNumber numberDiners ~ aNumber.

It is discussed in most operating systems textbooks.7 could not be written as follows: place: anltem counter signal. Explain why the method for place: in Figure 10. Explain why the method for retrieve in Figure 10. ~ 122 The Language Further Reading Many of the concepts discussed in this chapter.. and modify the values of the chopsticks array only in a critical section. Peter Deutsch in the special issue of Byte devoted to Smalltalk (Byte 81). 4.- . mutex critical: [ items addLast: anltem ] 2. EXERCISES 1. such as those described in Chapter 7? Produce a simulation for the Ice Cream store of Chapter 7 using processes.7 could not be written as follows: retrieve cou nter wa it. are adapted from a paper by L. Note that the easiest way to do this would be to introduce a monitor for the chopsticks semaphores. The dining philosophers problem was originally stated and solved by Dijkstra (Dijkstra 65). for example the notion of mailboxes.\. mutex critical: [ t items removeLast ] 3. Write a solution to the Dining Philosophers problem in which each philosopher is allowed to pick up his chopsticks only if both of them are available. How might processes be used to provide an alternative method for doing simulations. The version used here is taken from Peterson and Silberschatz (Peterson 83). _.

\ . .'l:.\.

0 .~~.~ V f~ " / ~ < < < < < < < < < < < < < < < < < < " v !\ ./ »>'»> »--.: ~: -jC 8: * -jC ~ '#:: '-2. '0 j~ ..\ ~~ "~ ~ :~ ~ : : ::::: ~««««««««««~.\ v ~~~ V ~««« ««««««<~ ~: ~ ~ """"'/""'/""""""""""/ §: //// / ///////// s: /~ .~ . 1\ 1\ i.~ '~ ~ : § ~ »»»»»»»»»»»»»»»»»»»»»~...~ ~: »))))))))))))))))))))))))))))))))))))))))):~ ~ i: f ~ ~ ~ ~ ~ ~ §> > > > > > > > > > > > > > > > > > 0 '0 ~ .~ \ ~ ~ t ~ Z: 0 ~ % v . ~ : ~ §{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{W~ ~ ~ ~ '0 .~ ~ ~ ~ §""""""""""""""""""~ ~ ~ ~. : ~ 0 ~~ ~ j 2 («((((«(«( («((((((((««« ((« (((««(«:.8: :!~.& ::j}: .. ~ : § §// / / / / / / / / / /// / / / /§ §: :* - *:::::::: ...»~.~ ~: ~ ~ ::=::: % % ~.~\ . ~ :~ ""'~ ::=:::-jC ~ ~. :::::: % .1. \ ~~ . . ~ j ~/ / / / / / / / / / / / / / / / ~ j 3"""'"" " " " " " " " " " " '"..::::: -l< *:::::::::: .. " / " / §»»»»»»»»»»»»»»»»»»»») '""' \j !\ ::::::..~ ~~ ~ j ~ ~. ~~ ~\ ~ :/ }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~ ~: ~ ~.:~ ~ . / ~ _.\ :~::j}: "'\ "~ "..3) ~ : """"'" """"""""'" """"~ 2: / / / / / / / / / / / / / / / / / /~ / / / / {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{1~ ~: ~ ~.~ ~ : ~ §}}}}})}}})}}}})}})}}}}}}}}}})}}}}}})}}}}}}}}}}}~ ~ ~ ~ ~ .."\ ~\. j I> -.~ ~ ~ ~.§... . ":::::J V /\ ~~ " !\ 1\ " " / / / V \ f •..~ ~ ~ ::j}: ~ : :..' '..~ " / .r ~ ~"""""" "-. ~ j ~ ~ 0 j~ ~ ~ ~ ~ ~«««««««««««««««««««««(j~ ~ ~ ~ ~' v ~~ ~ j " ~ V ..\ :\{t' .~ \~ .>.. ~ " / \?/ <S.~.:. . § §»»»»»»»»»>:2 ::: § §// / / / / / / / / / / / / / / / /2 !\ ~\ ~.~. ~ ::::: §»)») »)» »»») »))»))))»))))~ §: »)) »))) ~ . ~ ~~ ~ j ~{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{­ ~~ ~ j ~/ / / / / / / / / / / / / / / / / j~ ~ j ~""""""" '"" " " " " " " " "- j §}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} « « « « « « « « « « § ~: > > > > > > > > > > > > > > > > > > > :::~ " " " " V V V V ./ .\ " /\ '" / / ~ ~ ~ ~\%.~' ~ ~ ~ ~{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{i~ ~: ~ ~. H}}}}} }}}}}}}}}}}}}}}}}}}}}}H}}}}}}}}}}}}}}}}}:~ ~: ~ . \! ~ . '!. ~\::. ~ : :.....•.- \. . \ j § " j ~ '0 ~ .:::::: . './ " / §H}}}}}}HH}}}}}H}}}}}}}}}}}}}}}}}}}}}}}}}}} ~ j ~««««««({«««««««««««{«(! I 3«« < < «<< < < < ««< §»»»»»»»»» ~.. -jC -jC ~ ~««««««««««::: ~.:. (\ .. v ~..' ~ j 5{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ ./'..~ ~ ~ f ~ ~ 0 ~ . {«««««««««««««««««««««(§ »»»»»»»»»»»»»»»»»»»»»»§ §: {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{~~ ~ ~ ~.~ ~.8. ~ ~: :~ ~ !.'..\ \ j § : : ~ \ ~ 8 .§..-..·V!\ 8 .>'-. §: ~ ~ .::::: * . * :::::::::»» . '0 j( :: // §««««««««««««««««««««( ~~: 0 ~~ ~ ~ §)»»))»)»)»)»»)»)»))))»»)»»)) -jC * % ~ ~ ~ l2j ~.. '0 ~\ ~ j ~ ~ 0 ~~ :~ : ~ ~ ~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}r§ §: ~ ~. . I A\ 0 e/ " 0 t: t: / / " V g~ »»»»»»»»»»:: ~ ~ \! to j \ '0 j. / --.:' j // ~ j j ~ '0 j~ . " " " " " " """" " .~ " /j " ~ / ~ / § 2 § g§ :: / § §((({ ««((((««« ««((((((«(«((( «(((«~: ~ is V ~\ ~ / g§ g j ~ ~ ~.

<: :> '... :.. // '".:: ::... ...."-...:::::: ::::: ~ :::=: / \/ \1 ~ ~~: <~ :." .. .:': ~~ ~ \! ~ \ \ \ \:::::: ::::: .. /'\ . (1/((1 (0 (II ((( ((I (1/(( (il ((I (I/(! ((( : :...:':: ....:::. .:..~? :. ......' :<..". .... :::::~..: :':_: .. ~ ~::~':......-. eLI~~.. ~ . .. . / " '... \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\:2 ~O .~ " "" '\ \\.. \ .../\ /\ \' f . / / / / / / / / / / / / / /:--:: ::::::: .'.....S .... /\ . \'\ '\ \ )..~~~ ... -"" \/ '! ....::.. ./1'####### ~ ./ / .:::::::~ .. I))))))))))))))))))))))))))))))))))))) ~ ./ 'y' :::::: ::::: - t.....::.---..... \.. / ......: '-yo-' _ A••./ ::::::: ::::: :::. \ \ \ \ \ " \ I..: ::'.:\...1\ r... 'J \/ 'i :\ .7::...-' ....> \/ \/ "/ /\.#:#:#.•• ~ "v~ .:~..~:.~~ ......: )))))))))))))))))))))))))))))))))) § § :::::: :::::: ~:: § :::::: ~ =:: ~ #######..: <<::.1 \/ \/ \..~ .. <~: (I:::::: /\ II i I /" '" ~: . . ~ ~ :$ . >':<: <. \/ \/ \/ \' ......·. :~:~:~ ************************:::::: ::::: 2= :::=: ~.. \. ::::::: ::::::::: ~ 2§.....::: :'.··v-. '-.... . -v-' /\ ################## ~ :::: ::~::: : ...~:::: ~::-- »»»»»»»»>~ § ~ ~ ...2 ........ ..' . . :3... ..... / .-../ /' / / ~ . . ...m. ).::' . " .. / / "'-..... .:: k*******-****-********-**"'..'~..-' -... : :.. .: (it (Ii til (IL:::::: ::::: :: :::::.... ... ' ..:. .../'\ / .~.=. ::<: 1\ ~~~= <~<:«««««««<:::::: ::::::: ~ ~ ~ () ::::::: ::::: ~.. \ \ \ \ \ \ \ \....:-: " :::::: . .~. --..--....=:.. .. / .

: \ ..- \ ~=:.."\ ~} \.

.»>:> »»::-'> CHAPTER /////////// .0: . -}}}}}}}}}}}}}}}}}}}}}}}n.{{{ {{{{{{{{{{{{{{{{{{{{{{i '"'" '" '"'"'" '" '" " '''.\ ..>.'. ~ ~.-----.... ««({««««««««(..- n}}}}}H}}}}}}}}}}}}}}}}} ~({«««««««««({l ))))))))))))))))))))))) 127 . 11 Implementation .OvenJiew ////////// ~{{ {{ {{{{ {{{{{ {{ {{{{{ {{{{ { "- '" '" '" '" '" '" '" "'"..----.

and a different memory allocation policy must be used. variables are either global or local. Little Smalltalk is an interactive system. B. Prolog. but such basic features as class descriptions may change dynamically during execution. In an Algol-like language. objects exist 9utside of procedure invocation (if we take message passing to be the! SmaIltalk equivalent of procedure invocation) and may persist for indefinite periods of tilne. such as Pascal. Smalltalk is a multi-processing language. fl The following sections will outline some of the more important ways in which the design of Little SmaIItalk deals with these features. a stack (sometimes called an activation record stack") can be used to maintain local memory locations. SmaIItalk is interactive. it is possible for a user to specify a number of different processes and have them execute concurrently. Any identifier can be used to refer to objects of any type and can be changed at any time to refer to objects of a different type. in Smalltalk. In Smalltalk. ~\ ~. a Pascal compiler or an interpreter for BASIC. ~\ 128 The Implementation In order to better understand the reasons for many of the design features of the Little Smalltalk system it is important to first consider what features of the Smalltalk language force the implementation to be different from. In common with implementations for many other modem programming languages. are the following: D D D D Smalltalk is typeless There is no notion of a "declaration" of identifier type in Smalltalk. Among the more important aspects of the language. say. Thus.} "\ ~\ . This means that not only is the user free to create or modify identifiers at run time. Since procedures activate and deactivate in a stack-like fashion. from the implementor's point of view. Global identifiers exist all during execution and can thus be assigned static memory locations. if run time execution speed is to be kept fairly consistent. on the other hand. Objects have unscoped lifetimes. such as APL. or SETL. Local identifiers exist only as long as the procedure in which they are declared is active. Thus. As we saw in the last chapter. a stack-like allocation scheme is not appropriate. The remaining chapters will then deal with the implementation in more detail.. . no portion of the system may be tied too strongly to any particular feature (such as a class description) that may later be modified."\ . Thus the Little SmaIItalk system must make it easy to transfer control from one process to another.

memory for variables in a procedure is allocated when the procedure is invoked and can be released when the procedure returns. Unscoped Lifetimes In PascaL as in many other languages. any superclasses) to search for an appropriate method. for example. as memory locations are set aside. such as PascaL all identifiers must have a declared type known at the time the program is parsed. namely the class object. If.I1all. the number of different classes that could be defined is virtually limitless. this tag field can be similarly sJ. eight bits. I tag I value In languages where the number of data types is fixed and rather small (such as many LISP implementations). In Smalltalk. since the type information is known to the compiler and code can be generated accordingly. The conventional solution is to associate with the memory for each identifier a small tag that indicates the type of object being held in the value field. a procedure P calls a pro- . such as Smalltalk. Fortunately. Thus each object in the Little Smalltalk system can be tagged with a pointer to the appropriate class object. The next chapter will explain the internal structure of Little Smalltalk objects in more detail. the system uses the class pointer to examine the class (and. value In a typeless language.Implementation Overview 129 Identifier lYpelessness In an Algol-like language. via another pointer in the class object. for example. class pointer value To determine if some operation (message) is appropriate for some object. the type of an identifier generally cannot be determined at the time a program (or class description) is parsed. it is necessary to allocate space only for the values. the only notion at all comparable to the concept of type is the class of the object indicated by the identifier. for every class there is a unique object maintaining information about the class. Thus. on the other hand. during compilation. either at load time or at run time.

with new memory being allocated on top of the st. rather limited. some of which are being used and others unpsed (Figure 11. it is important that memory for objects no longer being access~d be reused for qew objects. objects can be created at any time and may persist for an indefinite period of time. This important portion of the system will be described jn more detail in Chapter 12. Thus.1 0 Static view of Pascal memory (js~ge stack growing upwards t memory for procedure Q m~mory for procedure p bottom of stack .130 The Implem~ntation cedure 0. the memory for 0 will be allocated after the memory for P and can be released prior to that of P (since Q must return before :P can return). In Smalltalk.1). The memory manager is thus an important component of the Little Smalltalk system. The memory manager handles all requests for memory and notes when memory is no longer being used. Since physical memory is.lck as each procedure is entered (Figure 11.2). Fi~Llre 11. at any time.. Thus a stack can be used. on mo~t systems. This calls for a more sophisticated memory allocation protocol. memory can be viewed as a sequence of locations. we have already noted.

such as a traditional compiler.2 0 Static view of Smalltalk memory usage memory for object nil unused memory for object x unused memory for object true memory for object false unused An Interactive System The internal representation of Little Smalltalk objects represents a compromise between two competing goals. must keep a great deal more information around than a batch-like system. For example. . requiring that it repeatedly parse statements prior to execution. Note that an interactive system. On the one hand. if nec- . modification. Whereas a compiler can ignore and delete the textual representation as soon as an adequate internal form has been constructed. This. such as the Little Smalltalk system. the representation must be flexible enough to provide ease in creation. however. it cannot be so general as to greatly degrade efficiency. and removal of objects.\ > ". an interactive system must be able to regenerate the original text. On the other hand. however.(~ \ \ Implementation Overview 131 Figure 11. would seriously slow the interpreter. An important special case. is the representation of objects of class Class. The internal representation of objects was briefly discussed in an earlier section and will be explained in more detail in the next chapter. if methods were kept in their original textual form they could be easily modified. which must include a representation of class methods.

Whereas the real machine on which the system is executing deals with resources such as bytes and words. Like nlost interpretive systems. Unfortunately. When a message is to be . using a conventional editor. A Multi-Processing Language The fact that Smalltalk is a multi-processing language produces a number of difficulties. and the method associated with that message passes a second message two to another object b. however. Even in the single process case temporary and argument variables do not necessarily come into existence and die in a stack-like fashion. Another option is to keep both the internal representation and the original source level textual representation in memory.\ 132 The Implementation essary. then the second message must return before the first message returns. objects. In order to execute properly. can be thought of as an assembly language for a special purpose virtual machine. such as those that correspond to conventional activation records. The interpreter for bytecodes is described in Chapter 14. if the message one is passed to an object a. An efficient. would require too much memory for small machines. Thus storage incurred as part of message passing. the internal representation of the procedural (or executable) portion of a class. and symbols. the implementation of blocks causes problems. where three options present themselves. could be allocated and deallocated in a stack-like fashion similar to activation records in a conventional language. the virtual machine can deal with higher level concepts. Also. a block must have access to the environment (including argument and temporary variables) in which it was defined. For example. for example. even though objects could persist indefinitely. to edit the class description. This is most obvious in the case of class descriptions. If the user wishes to edit the class description. The only restriction then is that class descriptions cannot be created. -a block can be passed back as a result of a message or assigned to an identifier and thus outlive the message in which it was defined. such as identifiers. this view is too simplistic. You might think that if Smalltalk did not permit multiple processes. such as stacks. This. as well as to internally generated objects. such as storage for arguments or temporary variables. the file is opened and edited. namely the class methods. but must exist in some file. Even without multiple processes. at least the message passing protocol would exhibit a stack-like behavior. although slightly less general option is to keep as part of the internal form of a class object the name of the file from which the class description was read. The solution is to uniformly apply the techniques of the memory manager to those objects corresponding to values that the user can see. The assembly language for this virtual machine is called bytecode and is discussed in Chapter 13. The first option is to re-generate a textual class description from the internal form if required.

Since there can be many processes. all active processes are linked together (Figure 11. The structure of the process handler will be discussed in Chapter 14. A search is then made of the class descriptions to locate the bytecodes associated with the method for the message. Figure 11. the context they will use during execution. When an interpreter executes a bytecode instruction that involves sending a new message.4). A context (Figure 11. called an Interpreter. The name Interpreter is a slight misnomer.3 0 A typical instance of class Context t--------? rece iver context I-------? arguments t---------+ temporaries and block parameter locations . and an interpreter stack used by the virtual machine that is executing the bytecodes (Figure 11. a better name might have been lnterpretableMethodReadyForExecution. is created. an object of class Context is created. The context also provides space for any temporary identifiers or internal parameters (such as for blocks) that will be needed by the message. a second type of object.5). Once the bytecodes are found.3) is an array-like object that points to the receiver of the message and the argument objects passed with the message. Instances of class Interpreter point to the bytecodes they will execute. a new instance of Interpreter is created and linked to the existing interpreter.Implementation Ove11Jiew 133 sent. which then becomes inactive until the message returns: new interpreter old interpreter A Process is then merely a pointer to an active interpreter.

...134 The Implementation Figure 11.5 0 A linked list of Processes 1 process 1 interpreter process 2 interpreter process 3 interpreter J .+ bytecodes 1---------+ internal evaluation stack Figure 11..4 0 An instance of class Interpreter 1---------+ context interpreter f ..

Implementation Ovetview 135 System Overview The impkmentation of the Little Smalltalk system. The driver reads the commands typed at the terminal by the user. Special Objects Class Parser In jtia Iizatiof"! Termination Primitive Handler Driver Process Manager Interpreter '-----. One special process is the driver.6 0 An overview of the Little SmaJltalk system . This section will describe in broad terms the various components and their interrelationships. like almost any large software system. Figure 11. .6 illustrates the major components of the Little Smalltalk system and their control flow relationships. a process is a sequence of Littl~ Smalltalk statements plus the context information necessary to interpret them correctly.- Execution - Courier Memory Manager . As we saw in Chapter 10. is a collection of interacting components. The process manager maintains a queue of active processes (recall that multiple processes can b~ created by using the message fork or newProcess). Central to the entire system is the process manager. cre'ates a process to execute Figure 11. insunng that each process is given afair share of execution time.

this routine then cleans up various object references kept in global variables and. To do this. reals. the primitive manager uses a set of special object routines. Internally. Bytecodes representing primitive operations are processed by a special module called the primitive handler. and starting execution."\ \ ~'} ~\ \ ':- ~~-:- 136 T/:ze Implementation each. and places the process on the queue managed by the process manager. The courier creates an interpreter to evaluate the message and places it on the process manager queue. To accomplish this. The memory manager is in charge of creating objects and keeping track of which objects are currently in use and more importantly. including reading in the standard library of Little Smalltalk classes. One of the most common tasks of the interpreter is the sending of a message from one object to another. if required. the initialization and termination module is the routine first given control when the Little Smalltalk system is started. suspending the sending interpreter until the receiving interpreter has returned a value and terminated. Finally. creating the driver process and placing it on the process manager queue. The task of this module is to set the values of certain global variables to the correct initial states. II II . which objects are no longer in use and thus can have their storage reclaimed to create subsequent objects. such as integers. each particular to a different type of object. The interpreter is in charge of executing bytecodes and updating the context for each process in an appropriate fashion. Also the primitive handler manipulates objects. for which the underlying representation is different from that of normal Little Smalltalk objects. produces statistics on memory utilization. Subordinate to the driver is a special module for reading and translating class descriptions into the form used internally by the Little Smalltalk system. and symbols. When the user indicates that execution can terminate. The primitive handler is the main interface between the Little Smalltalk system and the underlying operating system. Subsequent chapters will describe in detail the design and implementation of each of these components. Underlying and pervading all portions of the system is the memory manager. instructions describing the message to be sent are given to the courier. strings. Little Smalltalk statements are kept in an internal form called bytecodes.

»»»»»> ////////// CHAPTER ""'''''''''''''''''''' {{{{{{{{ {{{{{ {{ {{{{{{{{{{{ .\. »»»»» ////////// "''''''''''''''''''''' ~{{{{ {{ {{{{{{{{{{{{{ {{ {{ {{ n}}}}}}}}}}}}}}}}}}}}}}}} r«««««««««««1 ))))))))))))))))))))))) 137 .}}}}}}}}}}}}}}}}}}}}}}}}r «««««««««««(! 12 The Representation of Objects .

In the Smalltalk-SO system the instance variables from class Cclass are accessible. which defines instance variables n. and r. j. this chapter concludes with a description of the memory manager. part of the class description for Aclass but is inherited from class Bclass. class Cclass is a subclass of Object (which defines no instance variables). When we execute the associated method in class Bclass. dobedo. Suppose variable a refers to an instance of class Aclass. Figure 12.. is a subclass of Cclass. and m. Next suppose we pass a message. p. and r defines instance variables k. which defines instance variables k. Finally. This message. 1 It would be convenient if the representation of instances of Bclass (or Aclass) did not depend 1. As an illustrative example of the representation of objects. This chapter starts by describing the internal representation of most Smalltalk objects. Little Smalitalk differs from the Smalltalk-SO language in this respect. Land k . instance variables i. and k. Examples of special objects are instances of class Integer or Symbol.138 I The Implementation Fundamental to understanding the operation of the Little Smalltalk system is an understanding of how objects are represented internally. the only instance variables available to that method will be those of class Bclass not those of either classes Aclass or Cciass. in turn. l. Following the description of special objects. This class superclass structure is sh9wn in Figure 12.1.1 0 The c1ass-superclass hierarchy defines instance variables n. I. the so-called special objects. p. to a. Assume that class Aclass is a subclass of Bclass. Class Bclass. as part of its definition. however. A few classes of objects. consider a class Aclass that includes. have a slightly different representation since their memory must be able to contain non-Smalltalk values. and m defines instance variables i. is not implemented as.

even to the extent of eliminating instance variables. we need determine only the number of instance variables for the class of the object and need not consider any information contained in Figure 12. let us call the unnamed object b-object. Henceforth our terminology will be somewhat ambiguous. how can we represent methods in class Bclass so that changes to class Cclass do not force us to make changes also to class Bclass.) Similarly b-object would contain a pointer to an instance of class Cclass. The solution in Little Smalltalk is for the structure of each object to mirror its class structure.2 0 The object-superobject relationship includes instance variables n. For example. That is. In an analogy to the class-supercIass relationship. similarly. and r includes instance variables k. P. and m & c 1 U d e s instance variables i. since at any time the class description for Cclass could be modified. The context will determine the exact meaning of the term.2. When creating an object. c-object a superobject for b-object. We will sometimes refer to the complete structure as shown in Figure 12. (Since it is difficult to discuss unnamed objects. Thus a basic problem in constructing an internal representation for classes and objects is devising a scheme that permits the representation of a method for a class in a manner that is independent of superclasses.The Representation of Objects 139 upon the representation of Cclass. Thus the structure of an object can be described as shown in Figure 12. the object a possesses a pointer to an unnamed object that is an instance of class Bclass. j. and k .2 as an "object" and other times use the same term for each of the individual components. we call b-object a superobject for a and. I.2 may contain instance variables but only variables appropriate to the class of the object. Finally cobject contains a pointer to an instance of class Object. Each of the objects in Figure 12.

The final portion of each object is a list containing the values for each of the instance variables in the object (which are. An integer is contained in the object indicating the number of instance variables the current object contains. a single structure can be used to represent structures of any size. The 2. in truth. during development.. The purposeful abuse of arrays shown here is not presented as a principle to be widely applied. *inst_var[1].\~ '\ ~\ \ . code where tricks such as this are used must be all the more carefully examined to insure each use of the inst_val' array is correct. It is in fact quite easily a source of very inscrutable errors. . as follows: struct obj_struct { int int struct class struct struct obj struct struct obj struct }. (As a special case. and great care must be taken to insure that each time we index into the inst val' array valid information will be found there. 2 There are two objects that can "represent" a receiver of a message. And. as just described. The inst var array can be indexed arbitrarily to obtain any desired instance variable. size. Tricks of this kind should only be used after careful consideration has removed all more transparent alternatives. instance variable n The reference count field maintains a count of the number of pointers to the object and is used by the memory manager to discover when the memory used by an object can be recovered (when the reference count reaches zero). Since C does not generate code to perform subscript checking on array bounds. *super obj. The class pointer points to the instance of class Class that contains the description of the class of which the current object is an instance.. *c1ass. The superobject pointer points to the instance of the superclass of the current object class. We will discuss this in more detail later in this chapter. - ref_count. instances of class Object contain a null pointer in this field). pointers to other objects).> \~\ 140 The Implementation the superclasses. The structure of each object in the Little Smalltalk system can therefore be described as follows: reference cou nt number of instance variables class pointer super object pointer instance variable 1 .

the search for the corresponding method would begin in class Aclass. it must contain a reference count. a concise representation for these objects can reduce substantially the size of the entire data area and. In the example we have been using. or symbols. usually far exceeding all other types of objects. The class of the receiver of the original message gives the location of the search for the former. - Special Objects Note that the instance variables in the objects described in the last section are. at the level of C structures. A basic problem is how to tell if an object is or is not a special object. The second is the internal object which is an instance of the class in which the method executed was found. Thus both objects must be available to the interpreter. a would be an example of the first type. Therefore. Fortunately the number of such special objects is small and cannot be increased by the user without modifying the system. in many cases. on the other hand. dramatically alter the speed of the system or the size of the programs that can be executed. We will discuss this more in a later chapter. If the method for dobedo sent a message to self.The Representation of Objects 141 first is the object to which the . if it is. Both objects are important in understanding the meaning of messages sent to self and to super. A message sent to super. what type of object it represents.3 lists the special objects in the Little Smalltalk system. Examples of such objects are integers. Figure 12. then. The f prefix on the ref count field and others is used in deference to those C compilers that demand unique field names on structures.message was actually sent. We will illustrate special objects by using the example of the class Float. whereas the class (actually the superclass) of the object that actually responded to the original message gives the location of the search for the latter. The "obvious" solution is to . and. instances of which must be able to contain a C "double" value. would require a search to begin in class Cclass. Since each instance of class Float is an object. floating point numbers. merely pointers to other objects. At the very least. the number of instances of these objects can be quite large. and the unnamed b-object would be an example of the second. there must exist some objects in the Little Smalltalk universe with memory containing not other objects but values that mimic values in the underlying machine representation. structures for class Float must contain the following: struct float_struct { int double }. Nevertheless. Although the variety of special objects is small.

suppose we choose . \'. we can use a negative number to designate special objects."\.. In a single table it would be difficult to keep enough information to recognize that both of these instances were indeed special objects.31415 to represent the special objects of class Float (the choice of number is unimportant. For e~{­ ample.. This solution. .. there is nothing that prevents a user from altering a special value class. instances of the old class Float must also be recognized as special objects. Testing to see if a size field is less than zero is sufficient to determine if an object is a special object or not. however. is unworkable. Special objects are created either by the driver calling C routines or by invoking primitive methods. therefore. By looking up the class of any particular object in this table we can tell if it is special and what type of object it is. special objects cannot include instance variables). and. We can define macros to perform these operations. 142 The Implementation Figure 12. Nevertheless. Since the memory of special objects does not provide for the use of the inst_var array (and. \ . and thus it is easy to insure that proper numbers are maintained. If the user does modify one of these classes such as Float. then new instances of class Float should point to the new class description..3 0 Special objects in the Little Smalltalk system Class Block ByteArray Char Class file Float Integer Interpreter Process String Symbol Use code blocks bytecode arrays single characters class descriptions external files floating point quantities integer quantities interpreters (bytecodes in execution) processes string values symbolic values keep a table with the class for each special object. Different negative numbers can be used to distinguish the different types of special objects. by implication. Testing to see if it is a particular value will tell what qrpe of object it is. Normal objects will always have a size field that is zero or greater. A non-obvious but more workable solution is to use the "size" field to mark special objects. as long as it is distinct from . The Little Smalltalk system makes no distinction between classes for special objects and other classes.

the ciass description of special objects is not likely to change during execution (in contradiction to some comments made earlier). We can define the following macros: # define FLOATSIZE -31415 # define is_bltin(x) «(object *) x) -'-'. was a major factor in this decision.struct irit int double }. By keeping a table of special object classes and superobject~. we can eliminate the necessity of keeping this information with every object.- of Objects --'I 143 the numbers for all other special objects). all floating point values that existed prior to the change will have their classes altered. system. Since special objects are by far the most common form of object in the. Also note that this scheme works only because the superclasses for classes representing special objects do not contain instance variables. type) «(object *) obj) .>size = = type) # define is_float(x) check_bltin(x. f_value. has the unfortunate consequence that should the user redefine a class such as Float. an instance of class Float has the following structure: # define FLOATSIZE -31415 struct float-'. The macro is_float determines if an object represents a instance of class Float. Nevertheless after considerable debate on this issue. the representation for each object must contain a superobject pointer. a small reduction in the memory requirements for special objects may have a considerable impact on the total amount of memory needed for execution.. 3. The f_size field should always be FLOATSIZE. However. FLOATSIZE) The macro is_bltin tests to see if an object is speciaL Note the use of a cast to insure that the size field can be applied to the object. t f_ref_count. Should special objects contain a class and/or a superobject pointer? Arguments can be made both ways. respectively. it was decided to use the more space-efficient representation in Little Smalltalk. . This. such as the DecPro 350 or the IBM PC. For the sake of uniforrriity and consistency the answer should clearly be yes. All other special objects are treated similarly. The superobject and class of any special object can be determined by a pair of procedures: fnd_super( ) and fnd_class( ).3 Internally. ~- The Representation L. f_size. of course. In the one case where this is not true (the class String).\. The fact that we wanted Little Smalltalk to run on machines with very limited memory.>slze < 0) # define check_bltin(obj.

The first is a pointer to one of the special objects representing a class. and the third a flag indicating whether the instance variables should be initialized to the value of the pseudo variable nil. serious thrashing can result. A good discussion of memory management algorithms can be found in (Knuth 81). 5. 5 Free memory is maintained on free lists. If a structure is found on the free list.4 Therefore in the Little Smalltalk system great care is taken to reuse memory as much as possible. the free list for the object type is examined first. a free list is maintained for all objects containing less than a certain number of instance variables. There are two main classes of memory management algorithms. used. 6. When a reference count reaches zero. the second an integer indicating the number of instance variables to be allocated in the object. A free list is a linear-linked list of free memory structures. and therefore the memory it occupies can be recycled for use by another object. the instance nil or the class UndefinedObject? This difficulty is known as bootstrapping and is inde~d one of the tricky aspects of the Little . Each object maintains a count of the number of currently existing references that point to the object. not special objects). Which comes first. In addition.4. there are no remaining pointers that can reach the object. shown in Figure12. a general memory allocation routine is called to allocate new storage for the object.144 The Implementation Memory Management As execution progresses. An array is defined. since objects have different lifetimes. the object is returned to an appropriate free list. New_obj takes three parameters. the elements of the array being the head for a free list of objects of the given size. 6 4. the system would very quickly exhaust all available memory. and discarded. When memory for a new object is desired. If new memory were allocated each time an object was constructed. which is the main reason it is used in the Little Smalltalk system. unless some provision is made for reusing object memory on a virtual system. A method known as reference counting is used to accomplish this. The pseudo variable nfl is an object and is therefore presumably created by calling new_0 bj. reference counting schemes and garbage collecting methods. Objects are created by calling a procedure new_obj( ).. Virtual memory systems postpone this problem but do not eliminate it. It has the disadvantage that cycles can cause memory to be marked as being used when in fact it is not. When the reference count on an object reaches zero. Care is taken to keep these counts accurate. A reference to an object is simply a pointer pointing to the object. Reference counting has the advantage of simplicity. Can nil be initialized to nil? Similarly nil is presumably an instance of some class (UndefinedObject) that contains fields that must be initialized to some value. it is removed and used for the new object. otherwise. objects are continually being constructed.e.4. Careful readers will note a bit of circularity here. This array is obj_free_list. For normal Little Smalltalk objects (i. shown in Figure 12.

The solution involves first creating some objects that do not have a class and using them to create other objects which then can be used to overwrite the first object. 0. alloe) struct c1ass_struct *nclass.>inst_var[ i ] = o_nil). int nsize. new.> ref-.>super_obj. i < nsize.1) * sizeof(object *) ) struct obj_struct *new_obj(nclass.>super_obj (object *) 0. # define sizeobj(x) (sizeof(object) + «x) .4 0 The procedure new_obj() # define MAXOBJLlST 100 struct obj_struct *obj_free_list[ MAXOBJLlST ] . alloe. new . The procedure cant happen() is a bit of lfdefensive programming. int i.The Representation of Objects 145 Figure 12. new." designed to trap impossible situations that somehow do happen. to be described shortly). new ] new .>c1ass ). This routine is used throughout the Little Smalltalk initialization sequence.>size = nsize. . if (nsize < MAXOBJLlST && obj_free_list[ nsize ] ) { new obj_free_list[nsize]. Eventually a complete system is produced. be negative (special objects are created by other means. if (nclass) obj_ine«object *) new. if (alloe) for (i = 0. new.>c1ass nclass. { struct obj_struct *new. if (nsize < 0) eant happen(2). obj_free_list[nsize] } else { (object *) o_alloe(sizeobj(nsize». nsize.eount . i+ +) { obj_ine(new. ] return(new). = = = = = = The value nsize should never.

Each special object maintains its own free list. size = obj->size. When the reference count on an object indicates that its memory can be reclaimed. since the fields of the floating structure do not contain anything that could be used as a link. - } - - else { free(obj). We illustrate this with the free list routines for the class Float.6 takes a C floating point value and returns a Smalltalk object representing the equivalent value. ~ \ ~\ 146 The Implementation Smalltalk system. and the list is updated. The variable fr float contains the free list for these objects and is declared to be of type mem struct. returns it using the system memory deallocation routine. i + + ) obj dec(obj->inst var[i]). the routine free obj() (Figure 12.. i < size. ':. if (obj->class) obj dec«object *) obj->c1ass). it is removed. a new object is created by calling a general purpose memory allocation routine. the procedure new obj() next examines the object free list of the appropriate size. } ~\ "\ . Note the use of the super obj field as the link in maintaining the free list. After checking that the size is positive. } } .5) is called. if (size <: MAXOBJ LIST) { obj->super obj = obj free list[size]. The routine new_float( ) shown in Figure 12. int dofree. If there is some object on the free list.5 0 The procedure free_obj( ) free_obj(obj. dofree) struct obj struct * obj. If cant_happen( ) is ever called. if it is too large. If the number of instance variables is too large or if there is nothing on the free list.\. i. Free obj frees the instance variables used in-the object and then either places the object on the free list or. if (dofree) for (i = 0. an informative error message is produced and execution is halted. { int size. obj free IistTsize] = ob}. A cast is used to insure that variables are assigned values of the correct type. Figure 12.

6 0 Memory allocation routines for the class Float struct mem_struct { struct mem_struct *mlink' }. } free_float(f) struct float_struct *f. } = fr_float.The Representation of Objects 147 Everytime a new reference to an object is created. { jf (! is_float(f» cant_happen(S). we must increment the reference count field for that object. since there are still valid refer- Figure 12. The procedure obj dec() decrements the reference count for the object. This is accomplished by a macro obj inc() shown in Figure 12. . struct mem_struct *fr_float = 0. if (fr_float) { new = (struct float struct *) fr_float. { struct float_struct *new.produce a new floating point number */ struct obj_struct *new_float(val) double val. whenever an object reference is deleted. return( (struct obj_struct *) new). new->f_size = FLOATSIZE. fr_float = fr_float->mlink. /* new_float . Similarly. nothing further needs be done. new->f_value = val. } new->f_ref_count = 0. If the resulting value is still positive. «struct mem_struct *) f)->mlink fr_float = (struct mem_struct *) f.7. the routine obj dec() is called. } else { new = (struct float_struct *) o_alloc(sizeof(struct float_struct».

} } ences.> ref_count) > 0) return. Otherwise the reference count field is zero. and cant_happen( ) is called. 1.-(x . Because a large percentage of execution time is spent in performing this task. leaving the details of implementation to the imagination of the reader or to those ambitious enough to dig through the code. . In this section we will merely mention some of the approaches taken. if (is_bltin(x)) { switch(x .>super_obj) obj_dec(x .>size) { case FLOATSIZE: free_float«struct float_struct *) x). - Optimizations Memory management is a central task in the Little Smalltalk system. something is wrong with the system.>ref_count + +) obj_dec(x) strud obj_struct *x. 1). The underlying operating system memory allocation routines are slightly more efficient on large blocks of memory than on small blocks. default: cant_happen(6). } } else { if (x.7 0 Object reference increment and decrement routines # define obj_inc(x) «x) . { if (.>super_obj). and the space is recovered by calling either a routine specific to the type of the object or the general memory recovery routine. a great deal of attention has been devoted to speeding up the operation of the memory manager.> ref_count < 0) cant_happen(12). break.148 The Implementation Figure 12. if (x . free_obj(x. If the resulting value is negative.

According to the superobject scheme described in this chapter. we can reduce memory requirements substantially by keeping a single instance of this class and sharing references. 3. all objects should end in an unnamed instance of class Object. and then to carve it up into little pieces and place them on the free list. such as small integers or the pseudo variable nil. ~- The Representation of Objects 149 One scheme is to allocate a large block initially. which share a common instance of class Magnitude and Number. Almost all free lists used in Little Smalltalk are initialized in this manner. for example a block equal to 100 floating point structures. . occur frequently and seldom change. A single value can be maintained and reused as called for. Some constants. A similar trick is used for numbers. Since class Object does not define any instance variables.\. 2.

"\

.':0:.

_

»»»»»>
/////.//././)

CHAPTER

"""', "" '-, """" "" """" "
{{{{{HH{{{{{{{ {{{{{{{{{1

-H}}}}}}}}}} nn}}}}}n}}

«««««{«««{{««,

13
Bytecodes

/

.///././././././ /

"""{{{{{ {{""{{{{{"" """"{{{" '" "" '" {{{{{{ '
~{{{{

}}}}}}}}}}}}}}}}}}}}}}}} }}

((({{((((((((l
)))))))))))))))))))))))

150

Bytecodes

151

Like most interpretive systems, the Little Sinalltalk interpreter represents programs (in this case, class descriptions) internally in an intermediate representation. The. word intermediate refers to the fact that the code is between the very high level class description and the low level language in which the machine is actually operating. There are several reasons for this type of representation. One is compactness; the internal representation of a class description can be much smaller than the character representation used by the creator of the class. This is important since, for example, there are over 300 methods defined in the standard library alone. A second reason is efficiency; by translating the class description once into an intermediate representation and thereafter using the internal form, we avoid having to reparse the class description each time a method is invoked. With a good intermediate representation, it will be possible to construct a very fast interpreter. This chapter will describe the intermediate representation for methods used by the Little Smalltalk interpreter. This intermediate representation is known as a bytecode formaLl A traditional approach in designing interpreters is to define a virtual machine for a simpier language and then translate the high-level language into instructions for this simpler machine. Consider what such a virtual machine mig1).t look like for the Smalltalk language. Assume that at run time context information, such as the values of instance variables or temporary variables, will be available in the form of an array. We can arrange for this to be true and can define even the mapping between instance variables (for example) and an index into the context array when the class description is parsed. A convenient form of virtual machine is a stack-based architecture. In this style, intermediate results and temporary values are pushed onto or removed from a stack as required. Given these assumptions, the actions we would like to perform are as follows:
1. Access or modify instance variables. 2. Access or modify temporary variables. 3. Access arguments. 4. Access literals. Two special subcases of this are pseudo variables (since the meaning of self or super is context dependent and cannot be assigned at the time the method is defined), and class variables (since the class may not exist when the method is defined and the meaning of class variables can only be established at run time).

1. Note that the bytecode format used in the Little Smalltalk system is different from that used in the Xerox Smalltalk-80 system.

\

\

\.,.

\

152

The Implementation

5. Send messages. A special case of this is sending a message to self and to super. ) 6. Perform a return of some expression. A special case of this is the implicit return of self at the end of every method. 7. Create a block. 8. Perform a primitive operation. So there are approximately a dozen types of operations we would like to perform. We will eventually define a few more to permit some optimizations, but a limit of 16 different operations is sufficient. In each case the operation can be described as a tag, or opcode (to pursue the virtual machine analogy), followed by some other information. In many cases the other information is simply an integer (an offset into the instance.variable array, for example). In other cases the value is more complicated (a literal or a class name, for example). First let us consider how we might represent an operation such as referencing an instance variable. We have hypothesized at least 12, and no more than 16, different types of operations. So four bits are both necessary and sufficient to represent the operation type. Since we will want to sequence easily through a list of opcodes/values pairs and in these cases the opcodes and values can be represented by small integers, an array of bytes can be an attractive representation. It seems rather wasteful to devote an entire byte to each opcode, since each opcode requires only four of the eight bits in the byte. One scheme, therefore, is to encode both the opcode and the value fields in a single byte, placing the opcode in the upper four bits and the value field in the lower four bits. (Each four-bit sequence can be called a unibble" since it is a small byte.) Pictorially, this can be represented as follows:
width field

4 opcode

4 value

If, for example, we let 1, mean "access an instance variable," then the instruction to access instance variable number 3 would be the single byte with value I * 16 + 3, or 19. "'-'" An obvious problem with this scheme is that it permits the manipulation of only 16 instance variables. Classes can have more, but that is quite uncommon. A simple solution to this, and related problems is to have an "extended size" instruction. This would involve having some opcode (say, zero) which takes another opcode as a value. The entire next byte following this instruction is then taken to be the value field for the extended instruction. Pictorially, the instruction "access instance variable number 37" could be given as follows:

Bytecodes

153

width
value

4

4

o

8 37

The advantage of this scheme is that it can apply to more than just one type of opcode, and, furthermore, it allows us to keep the very short description in the large number of cases where the extended form is not necessary. Of course, the disadvantage is that we are still limited to 256 instance variables, but that is a reasonable ~ompromise. A simple solution to the problem of literals is to associate a literal array with each method. At parse time this array can be defined with whatever . literals are needed by the method. The value field for those instructions that require a literal is then just an index into this array. The following sections present a description of each of the instructions in our internal representation.
Extended Instruction Format - opcode 0

width
value

4

o

I

4 opcode

8

I

value

The low order 4 bits of the first byte are used as the opcode for the next instruction. The following byte (all 8 bits) are taken to be the value field for the next instruction.
Access an Instance Variable - opcode 1

width field

4

I

4 index

I

The instance variable indexed by the value field is pushed onto the stack. The extended instruction format can be used for instance variables with indices greater than 16.
Access an Argument or Temporary Variable - opcode 2

width
field

4
2

I

4 index

I

Both arguments and temporary variables are kept in a single array called the context (Chapter 11). The element of this array indexed by the value field is pushed onto the stack. As with the instance variable opcode, the extended instruction format can be used for indices greater than 15. By convention the receiver (the zeroth argument) is placed in the first position of the context.

:. )

l.:.

\

'>

\

~\

154

.The Implementation

Access a Literal - opcode 3

width field

4 3

4

I

index

I

The element of the literal array indexed by the value field is pushed onto the stack. Note that, since the literal array can hold any literal value known at parse time, this works for all types of literals (characters, integer, string, symbol, or arrays) with the exception of c,iasses, which must be generated at run time.
Access a Class Object - opcode 4

width field

4 4

4

I

index

I

At parse time, a symbol representing the name of the desired class is placed into the literal array. The value field then contains the index in the literal array of this symbol. During execution the class description corresponding to this symbol is retrieved and· pushed onto the stack.
Store into an Instance Variable - opcode 6

width field

4

4

6

I

index

I

The current value contained in the top of the stack is popped and stored into the instance variable indexed by the value field.
Store into a Temporary Vari~ble - opcode 7

width field

4 7

4

I

index

I

The current value contained in the top of the stack is popped and stored into the position of the context indexed by the value field. Although both arguments and temporary variables are stored in the context, the parser can make certain that no instruction which would overwrite an argument location is generated.
Send a Message - opcode 8

width value

4
8

opcode 15 width field 4 15 I 4 value The value field is used to indicate a variety of instructions that either do not require arguments or usually require arguments greater than 16. The following byte is interpreted as an index into the literal array. Return receiver. Bytecodes 155 It is assumed that prior to this instruction the receiver of the message plus the necessary argument values have been pushed onto the stack.opcode 14 width value 4 4 8 . Note that the object representing both self and super is the same and is given by the first position in the context array. If this value is nonzero. the second byte contains the position in the context where the arguments for the block should be placed. Return from inside of block. skip the number of bytes indicated by the next byte and push nil onto the stack.\. Send a Message to super . The value field contains the number of arguments to be passed along with the message.--_1_4 __1 argcountl argument location I 8 block size The value field of the first byte contains the number of arguments for the block. These can be described as follows: value meaning Duplicate top of stack.opcode 9 The fields are the same as in the previous message. and thus would not benefit from the short encoding. The extended instruction format can be used for messages with more than 16 arguments. The third byte contains the size (in bytecodes) of the instructions contained in the block. If it is true. If there are no arguments for the block. Pop top of stack. Pop top of stack and disca rd it. Create a Block . 1 2 3 4 5 6 . The instructions for the block follow immediately after this byte. Special instruction . The ·symbol stored at that location is taken to represent the message selector to be sent. the second byte is omitted.\ . Return top of stack.

If it is false. Message is at first location of literal array. Skip forward the number of bytes indicated by the byte immediately following this instruction. Send a message with one argument. the method: isEmpty t self size = 0 would be represented in the bytecode format in the following way: highBits 2 lowBits 1 Meaning Push the fi rst element of context (self) onto stack. and they will be described in the next section. Skip backwards the number of bytes indicated by the byte immediately following this instruction. The first element is a ByteArray containing the bytecodes for the method. skip the number of bytes indicated by the next byte and push true onto the stack. The Representation of Methods Opcodes 5 and 10 through 13 are used to provide a succinct representation for common operations. ~ \~\ 156 The Implementation 7 8 9 10 11 12 Pop top of stack. skip the number of bytes indicated by the text byte and push false onto the stack. Thus. Send a message with no arguments. The second element is the literal array associated with the method.\ )- "\ ~\ \ . Pop top of stack. The class of special objects ByteArray is used to represent arrays of bytes. Pop top of stack. 8 4 8 0 1 2 1 . The following byte indicates the number of arguments to accompany the primitive. Perform a primitive operation. If it is false. a method is translated into an array containing two elements. Instances of ByteArray are created using a syntax similar to normal arrays. for example. the primitive number. If it is true. with a square bracket instead of parenthesis: #[ 17 23 36 ] Internally. and the byte after that. Push the literal in location two onto stack. skip the number of bytes indicated by the next byte and push nil onto the stack.

The pseudo variable selfProcess. The second class of optimizations increases the speed of the Little Smalltalk system. The integer . One way to reduce size. Return top of stack. true. Complex. any savings in size will greatly decrease the amount of memory that must be devoted to storing the standard library and thus increase the size of programs the user can execute. ByteArray. common classes (Array. classes. consider the representation of the method for isEmpty described in the last section. 1. The pseudo variable false. and the pseudo variables. To understand why more succinct representation is necessary. therefore. Collection). Dictionary. and is of considerable size. literal array #( # size 0 # =) = )) This would be rendered entirely in Smalltalk format as follows: #( #[ 33 128 1 66 129 3 243 ] #( #size 0 # Optimizations There are two classes of optimizations. Class. while possibly limiting generality. Since the standard Smalltalk library is represented in the internal form. occur with much greater regularity than do any other constants.2). Boolean. The pseudo variable nil. Collection. We will use opcode 5 for this purpose. _ Bytecodes 157 15 3 3 Message is in third location of literal array. 1. Block. Some constants. such as 0. The following table shows how each of the values is interpreted. Char. False. 0. Bag. . The first type reduces the size of the internal representation of methods.\ S. 0-9 10 11 12 13 14 15 30 The integer value. and the pseudo variables nil. The value field of this special. false and smalltalk.opcode should be able to represent the most common integers ( -1. Arrayedcollection. One of the classes Array. is to encode with a special opcode a few of the most common integers. or nil. This is essentially trading a small increase in the complexity of the interpreter for a reduction in the size of many methods.t The pseudo variable true. The pseudo variable smalltalk.

If we use these new opcodes. True. of course. Nevertheless. Interval. Set. The. Float.. "\:. opcode 11 for binary messages. Radian. the bytecode representation for isEmpty becomes the following: highBit 2 10 5 lowBit 1 4 Meaning Push the first element of context (self) onto stack. in the class Collection the messages new new: value: do: class and error: comprise almost a third of all messages relayed. We will use opcode 10 to represent unary messages. but the following messages seem to be most common: unary messages new isNil notNil size class value first next print printString strictlyPositive currentKey binary and binary keyword messages new: at: to: do: value: = = ~~ timesRepeat: whileTrue: whileFalse: ifTrue: ifFalse: error: add: coerce: remove Key: addFirst: addLast: reverseDo: addAII: addAIILast: occurrencesOf: remove: binaryDo: keysDo: inRange: arithmetic messages + . Integer. In a similar fashion. Magnitude. o . Point.. SequenceableCollection. '1 Push constant O. Send unary message lI size. String. it merely reduces the size of the bytecodes and of the literal arrays.. or UndefinedObject Note that the class constants have value fields greater than 16 and thus must use the extended instruction form. using the value field to indicate which instruction is desired. ~\ \ . when we examine the bytecode representation of a few classes. KeyedCollection.-} \. Interpreter. opcode 12 for arithmetic messages. Note that this does not alter the meaning of the message or the way in which the message is processed by the receiver.\ ). differ from class to class. For example. and the form still eliminates the need for a position in the literals array. we note that some messages occur with much greater frequency than others. Symbol. Number. Object.* " "" bitShift: bitAnd: bitOr: < < = = ternary keyword messages ~ = >= > at:put: ifTrue: ifFalse: ifFalse:ifTrue: value:value: to:by: at:HAbsent: indexOf: ifAbsent: inject: into: remove: ifAbsent: removeKey :ifAbsent: A very powerful scheme for reducing the size of the internal bytecode representation of many methods is to encode the sending of these common messages by a single instruction. there is still a net size reduction since these constants are much less common than the integer values. and opcode 13 for ternary keyword messages. 158 The Implementation File.. distribution of messages sent will. Random.

. the result would be pushed back onto the stack. of course. the parser cannot be certain that the recipient of any iITrue: or ifFalse: message will be an instance of Boolean. the speed increases are so dramatic that some compromise must be maqe between maintaining the elegance and increasing the efficiency of the language. A great percentage of all messages processed by the Smalltalk system are represented by ifTrue:. from practical point of view a considerable amount of time is being needlessly used by the system in the overhead involved in message passing. ifFalse:. or their combinations. 2 Consider the sequence of actions to be performed in a conditional expression: a (3 < 7) ifTrue: [9] In the process of interpreting this expression. The top element of the stack would then be removed. Furthermore." Return top of stack. more importantly. An even more important type' of optimization is concerned with increasing the speed of the Smalltalk system. since there is no notion of types in Smalltalk. the literal array has been eliminated altogether. inte. While this contributes to the simplicity and elegance of the Smalltalk language. a great philosophical debate concerning whether this is desirable. One scheme that improves the speed of the Smalltalk system is to process conditionals and some loops with in-line code. Bytecodes 159 12 15 10 3 Send arithmetic message 11=. 2. There is. . In some other class.'. the boolean expression would be evaluated and pushed onto the stack. Nevertheless.' Either the block would be evaluated and its result returned or nil would be returned by the appropriate subclass of Boolean.\. In either case. According to some. Conditionals and loops can be implemented using nothing more than message passing. in Smalltalk the user should be able to change arbitrarily the meaning of any message. The next section will consider several of these optimizatjons.. Dynamic Optimizations The optimizations we have been considering so far have been concerned with reducing the size of the internal representation. the meanings of these messages could be' radically different.~ranging the meanings of iffrue: and ifFalse: for example. literal array #( ) So the size of the bytecode array has been reduced from 7 to 5 and. . and the message ifTrue: would be sent to it along with a block argument.

it is possible to encode whileTrue: or whileFalse:. The statement [block one] whileTrue: [block two] can be represented internally as byte co de representation of block one skip on false byte co de representation of block two skip back to the beginning of block one . instead of pushing nil onto the stack when a skip is taken.\ " \ \ '. To accomplish this another pair of special instructions. ~\ . there are no remaining opcodes.. called "skip on false. on the other hand. if it is true. The meaning of this instruction would be to pop and examine the top of the stack. is used. Unfortunately. Push the constant 9 onto the stack. Push the constant 7. the internal representation of our example would be as follows: lI highBit 5 lowBit 3 Meaning 5 12 15 5 15 7 8 7 1 9 2 Push the constant 3 onto the stack. the instruction terminates and the next bytecode is examined. Send the message # <. The following byte is then taken to be the value field. the interpreter can simulate these actions by using a "skip" instruction. There is a similar skip on true" instruction. Amount to skip. return top of stack The implementation of the logical connectives and: and or: is similar to that of conditionals. and-skip or or-skip. the representation of a conditional is still shorter than the previous representation using blocks and message passing.' . the value nil is pushed onto the stack and the location counter is incremented by the amount in the value field. either true or false is used." The value field of this instruction encodes the number of bytes to skip. the top of the stack is not true. except that. Given this scheme. If. Finally by introducing another pair of instructions. The body of the argument block can then be placed irnmediately after the skip instruction without creating a block.' 160 The Implementation Instead of using message passing. just as it is in the extended instruction format. Skip on false. which merely branch forward or backward by a specified amount. Note that even with this format. and. Using opcode 15 we therefore define skip on true and skip on false to be special instructions.

!\ /\ /\ 1\ /' -\ ~ ~ ~ ~ ~ ~ ~ ~ (8) ~~} * * * (81 ~ * * !\ * ««««« /\ /\ /\ @ ~ ~ ~ . . " »»»»»> ////////// CHAPTER '{{{{{{{{{{{{{{{{{{{{{{{{{1 " '" '" '"'" '"'" '" '" "- .~."''''''''''''''''''''''" ~{{{{{{{{{{{{{{{{{{{{{{{H }}}}}}}}}}}}}}}}}}}}}}}} }} {«««««««««««! ))))))))))))))))))))))) 161 .\ " f\ (8....c: .\...}}}}}}}}}}}}}}}}}}}}}}}}} «««««««««««(. 14 The Process Manager .8 ::H:: * * * * * * * * * * * * * * * * ... - 1\ 1\ i\ 1\ 1\ (8) (81 {81 (2: (~ -~ .»»»»» ////////// .~ '~~i ~ ~ ~ ~ f\ f\ .

. and it is the bytecodes that are actually being executed ~t any time.> . the actions of the driver are controlled by C code and not by bytecodes. Acting as a controller. the process manager can be thought of as an abstract datatype manipulating a circularly (and doubly) linked list of process objects (instances of class Process). The -doubly linked list controlled by the process manager -is known as the process queue.' \ . In the Little Smalltalk system. runningProcess. There is a global variable. figure 14.1. Except when the current interpreter is the driver. This chapter describes the interface between the process manager and the rest of the Little Smalltalk system. Each process points to a linked list of interpreters. the process manager is a central component in the Little Smalltalk system. and it explains the tasks the various routines perform. the process manager schedules the different tasks to be performed and insures that every process is given a fair share of execution. The interpreter indicated by runningProcess points to the bytecodes.1 D The process queue process 1 \ interpreter interpreter running Process process 2 interpreter process 3 interpreter interpreter ' interpreter I . which we will describe shorty. 1.' This sit..uation is shown' in Figure 14. In a certain sense. \ .\ .--=-: 162 The Implementation As Chapters 10 and 11 noted.. that points to the process curreptly being given control of execution.

p_ref_count. *interp. Passing the message newProcess to a block. Figure 14.BLOCKED 4 . the value of runningProcess will not.. A second variable.2 0 The internal representation of processes struct process_struct { int int struct interp_struct int struct process_struct struct process struct }. The global variable runningProcess has already been described. * next. # define is_ driver(x) (x_drive = = (object *) x) /* process states * / # # # # # # define define define define define define ACTIVE SUSPENDED READY BLOCKED UNBLOCKED TERMINATED o 1 . *prev. however.SUSPENDED 2 . the value of currentProcess will be moved to the next value in the process chain. Processes can exist without being on the process queue. The variable currentProcess is guaranteed always to point to a process that is on the process queue.'i:. The internal C structure of a process is shown in Figure 14.. p state. change until control is returned to the process manager. Processes are said to be in one of four states. \ . creates a process but does not schedule it for execution. extern struct obj_struct *0_drive. current Process.2. for example.\ The Process Manager 163 Processes (instances of class Process) are special objects and thus can have an internal structure different from other types of objects. If the currently running process becomes terminated. is slightly different. p_size. \.\ ) . extern struct process_ ~truct * cu rrentProcess. These states are: Active An active process is one that is on the process queue and will be scheduled for execution. extern struct process_ struct * ru nning Process.

It is unique because.} '. A process that is not blocked is said to be unblocked. The next section will describe the purpose of each routine by going through a typical sequence of calls. . the interface to the process manager is shown in Figure 14. the actions of the driver are produced by a C subroutine that reads commands from the terminal and creates other interpreters to execute them. Initially. A process can be blocked by a semaphore (see Chapter 10). the driver is pointed toby a global variable named o drive.\ 164 The Implementation Figure 14. A terminated process is one that has halted either because it finished execution or because it received an explicit terminate message. A process that is not suspended is said to be ready. . A terminated process cannot be restarted. there are no objects on the process queue. Newly created processes using the newProcess message are initially in the suspended state.3. a blocked process is not scheduled for execution. Like a suspended process. The initialization module creates an interpreter object (an instance of class Interpreter). A blocked process may be restarted by the blocking semaphore. state) create initial process queue start process manager change interpreter link on current processs create a new process remove all remaining processes from queue set the state on the given process Suspended A suspended process is not on the process queue but can be Blocked Terminated scheduled for later execution by passing it the message resume. instead of having its actions controlled by bytecodes. In terms of C subroutine calls.\ :'~ \ ~\ '. .3 D Process manager interlace init_process( ) start_execution ( ) link_to_process(anlnterpreter) cr_process(anl nterpreter) flush_processes( ) set_state(aProcess. when the Little Smalltalk system is started. Internally. This special interpreter is known as the: driver.

\. } else new = structa Iloc(process_struct) . assig n(cu rrentProcess . as follows: . with the single process and interpreter. . new->p_ref_count = 0.4). prev. sassi 9n(new . new->p state = SUSPENDED.>prev.initialize the process module */ in it process ( ) { struct process_struct *p. anInterpreter).>p_size = PROCSIZE.\. /* make the process associated with the driver */ currentProcess = cr process(o drive). which in turn calls cr process( ) to create a new process (Figure 14.> next. assign(currentProcess . CU rrentProcess).>prev.> next.\ s. cu rrentProcess . The Process Manager 165 Figure 14. { /* get a process either from the free list or from memory */ if (fr_process) { new = (process *) fr process. struct process_struct *new. currentProcess). (process*) 0_ nil). The procedure init process( ) then creates the initial process queue.create a new process with the given interpreter */ struct process struct * cr process (ani nterpreter) struct interp struct *anlnterpreter.4 0 Routines for performing process initialization /* init process . return(new). sassign(new. /* cr process .> interp.> p_state = ACTIVE. int i.>next. fr_process = fr_process . sassign(new . (process *) 0 nil). The initialization module calls init process( ). new .

The initialization routine will call flush processes( ). resu me(presentl nterpreter). Before that happens. which will remove any remaining processes from the process queue and execution will halt (Figure 14. uninterruptible) execution and will be described shortly.5 0 The main execution loop /* start execution . When the driver is given control (via test_driver( ). \ '\ \ ) " 166 The Implementation ru nning Process -----I~ driver interpreter The initialization code then calls start executione ). while (1) { /* advance to the next process unless atomic action flag is on */ if (!atomcnt) runningProcess = currentProcess = currentProcess-> next. if (! is driver(runningProcess->interp» { /* notthe driver. The procedure start execution() loops over the process queue and for the remainder of execution will select items from the process queue and execute them (Figure 14. atomcnt = 0. } /* test driver is passed 1 if it is the only process or if the atomic action flag is enabled */ else if (! test_driver«currentProcess = = currentProcess->next) break. When the user indicates there are no further commands (by typing control-D). resume executing the bytecodes */ sassig n(presentl nterpreter.6).\ .e. it is likely the user will type a number of commands. ru nningProcess-> interp). The flagatomcnt is used to provide "atomic" (i. start execution() will return to the initialization routine. as shown Figure 14.main execution loop */ start execution ( ) { struct interp_struct *presentlnterpreter.5). II (atomcnt > 0») .. however.

> next . aProcess . obj. 1* flush processes . safeassig n(a Process . make sure this remains true *1 if (aProcess = = currentProcess) currentProcess .>prev. a new instance of Interpreter is created to evaluate it.dec«object *) currentProcess). obj_inc«object *) aProcess). - { if (aProcess aProcess ..> prev. then decrement reference count *1 obj inc«object *) c~rrentProcess).> prev.> next) remove process(cu rrentProcess) . safeassi gn(cu rrentProcess . The driver sets a pointer in the new .> next. (process *)0 nil. (The next chapter will discuss how interpreters are created and executed in response to messages). In order to avoid having memory recovered while we are manipulating pqjnters.> next. aProcess .> prev). obj .> next).The Process Manager 167 Figure 14.flush out any remaining process from queue *1 flush processes ( ) {. - 1* remove_process .re!ll0ve a process from process queue *1 static remove process (aProcess) process * aProces~. safeassig n(aProcess . we increment reference count.6 D Process termination .> next) cant_happen(15).(process *) 0 nil) . wh iIe (cu rrentProcess ! = cu rrentProcess . in Figure 14.> prev . safeassign(currentProcess . 1* removi!1g last active process *1 == /* currentProcess must always point to a process that is on the process queue. I*prev link and next link should point to the same place now.5. then change pointers. the procedure test driver( ) will be described in the next section) it waits for a command to be entered at the terminal. obj_dec«object *) currentProcess).dec«object *) aProcess). . When the user has typed a command. currentProcess = obj_inc«object *) currentProcess).

safeassig n(ru nningProcess-> interp anInterpreter).7 0 The procedure Iink_toyrocess /* link to process .'. the courier creates a new instance of Interpreter to respond to the message and links this interpreter to the sending interpreter.0- \. giving the new interpreter as argument. would give control to the next process. we have the following picture: I runningProcess ~ process new interpreter driver interpreter I Control then returns to start execution() which. ( struct obJ_struct *temp. The courier then calls link to processe ) to modify the current processes interpreter chain.change the interpreter for the current process */ link to process (anlnterpreter) struct interp struct *anlnterpreter. The procedure link to process() changes the interpreter pointer on the currently executingprocess (the one pointed to by runningProcess) to be the argument (Figure 14. \ \ 168 The Implementation interpreter to point back to itself and then calls link_to_process( ). If there are no other ready processes. giving us the following picture: I ru nn ingProcess process receiving interpreter sending interpreter driver I Figure 14. if there were other processes on the queue. Suppose the method being executed by the interpreter requires a. the newly created interpreter is given control. I . After determining the recipient of the message. message to be sent. The interpreter signals the courier that a message is required..\ .7). Thus.

This context corresponds to an interpreter that may be several positions higher in the interpreter chain. The procedure set state( ) is shown in Figure 14. . Unlike the original interpreter chain. The Process Manager 169 The execution of a block (generated by sending the message value to an instance of class Block) causes a similar sequence of events. and then a return is performed from that location. When the interpreter encounters a bytecode indicating that a return should be performed. but simply with a null pointer for the calling interpreter.8. The message fork passed to a block is simply a combination of newProcess and resume.3. it passes as argument the interpreter towhich control is being returned. the interpreter chain for this new process ends not with the driver. When an interpreter chain ending in a null pointer is terminated (for example. ru nnin9 Process----+I process interpreter~---~Iinterpreter A return from within a block is slightly more complicated because the return must take place from the context in which the block was defined. by finishing execution) the assoociated process is removed from the process queue. set state(). Passing the message resume to this new process will place the process on the process queue. A search is made of the interpreter chain until the correct interpreter is found. is used by the classes Process and Semaphore (via primitives) toinsert or remove processes from the process queue and to terminate processes in error conditions (attempting to return from a block when the creating context is no longer in existence. . Because the classes Semaphore and Process themselves manipulate the process queue. it again calls link to process( ). this time however. Passing the message newProcess to a block causes both a new interpreter and a new process to be created.\.'. for example). there is the potential for dangerous interaction should the process queue change while a method in one of these classes is . The final procedure describe in Figure 14. A new interpreter is created to execute the statements within the block and then is linked into the current interpreter chain.

The command module examines the second character of the line to determine the command type and then processes the command. ~\ \. When the line grabber indicates that a complete line has been entered (by returning a nonzero value)..5. If the atomic action flag is set by invoking the proper primitive no other process will be given control of execution until the atomic action flag is reset. to read in the standard library.9. This may have the side effect of altering the location from which the line grabber 2. The initialization module uses some of the subroutines from the commands submodule during initialization. . For th~s reason. In terms of the process management routines. for example. the line is assumed to represent a system directive and is passed to the command module for processing. and disable atomic actions. nu II poi nter" executing. Thus the class Semaphore. 2 which. as we saw in Figure 14. was called by the start up routine. The line grabber routine buffers characters typed by the user until a complete line has been entered.l--. atomic actions are controlled by the global variable atomcnt (Figure 14. 170 The Implementation ru nn ing Process ------. See Figure 14. This is not quite true. the first character of the line is examined by the test driver() routine. there is a flag called the atomic action flag that can be set by processes. will enable atomic actions.) .\. The Driver The structure of the driver module is shown in Figure 14. test_driver( ) calls upon the procedure line grabber(). ----+[~J II new process 1----+1 interpreter 1------. S \ ~:. The procedure test driver() is called by the process manager to determine if a command has-been entered by the user at the keyboard..i interpreter 1-----. insert or delete a process from the process queue.10. If the first character is a right parentheses. . To do this.P_r_0-rc_e_ss-'I---'----.5).. for example. The interface to this module is through the single procedure test driver().

set the state on a process.. { switch (state) { case BLOCKED: case SUSPENDED: case TERMINATED: if (aProcess->p state = = ACTIVE) remove process(a Process) . reads input (in the case of the )i. for example). The Process Manager 171 Figure 14. it is -passed to the command line parser for decoding. int state. )e and )r commands. The task of the parser is to decipher the command line and produce an interpreter that will have the effect of performing the actions desired by the user. aProcess->p state I = state. case CUR STATE: break. break. break.\ . or of totally changing the values in memory (for the )1 command.. which may involve inserting or removing it from the process queue */ int set state (a Process.. To accomplish . } return(aProcess->p_state). - case READY: case UNBLOCKED: if «aProcess->p stateAstate) = = -ACTIVE) schedule process(aProcess). state) struct process_struct *aProcess.8 0 The procedure set_state() /* set_state . default: cant_happen(17). aProcess->p state &= state. The )i command will generate a new Unix process to parse the class description given in the command and then change the file examined by the line grabber to be the output of the class parser. If the line returned by the line grabber is not a command line. This will be described in more detail in the next section.

the system creates a separate Unix process that parses the class descriptions and translates them into sequences of simple Little Smalltalk statements. The class parser uses an LALR parsing algorithm.0. and the class parser described in the next section will not be presented here since they are quite lengthy. The Class Parser Class descriptions are handled in a rather novel way in the Little Smalltalk system. If no errors are found during parsing. is that class descriptions must be read in from a file and cannot be created directly at the terminal. Since the class parser lives in its own Unix process. as described in the last section.\ . \ 172 The Implementation Figure 14. . the parser. Instead of complicating the driver by requiring it to read and understand the syntax of classes. and the Little Smalltalk system can run on machines with very limited address spaces. The tradeoff is that the Little Smalltalk system will work only under operating systems that permit multiple-user processes. the lexical commands module.. the class parser is many times more complex than the simple parser used in the driver module. however. its size does not increase the size of Little Smalltalk itself. The sources for the line grabber. generated automatically from the grammar by a sophisticated parser generator. The class parser reads 3. Since the grammar is much larger. the parser calls upon the interpreter module to create a new interpreter and then calls link to process() to place the interpreter onto the process queue.9 D Structure of the driver module test driver line grabber line parser commands module this. 3 The limitation of this technique. a simple recursive descent parser is used.

== and parses a class description. /* indicates whether to use block or non-blocking input */ ( switch(line grabber( block» ( default: cant_happen(17). and the class is defined. *super class. .b(~struct c_ref_count. return(1). case 0: /* enqueue driver process again */ return(1). In particular. case 1: if (*Iexptr 1)1) ( dolexcommand(lexptr). c size *class name.The Process Manager 173 Figure 14. it is necessary first to see how objects of class Class are represented internally in the Little Smalltalk system. Instances of class Class are special objects (see Chapter 12).see if the driver should be invoked */ int test driver(block) int block. *file_name. } parse( ). To understand how instances of class Class can be created using Little Smalltalk statements. The file containing these commands is then read by the line grabber module. it then produces a file of Little Smalltalk statements that together create the objects of class Class corresponding to the class descriptions. return(1). If there are no errors encountered during parsing. the internal structure of Class objects is given by the following C structure definition: struct class_struct { int int struct obj struct struct obj-struct struct o.10 0 The procedure test_driver( ) /* test driver . case -1: /* retu rn end of fiIe i ndication * / return(O). and thus are permitted to have internal representations different from other Little Smalltalk objects.

using the same index. the number of instance variables used by the class). super_class and file_name fields are each pointers to objects of class Symbol. As we noted in Chapter 12. context size. The second element is an array of literal values used by the method. The first element is a ByteArray containing the bytecodes for the method.174 The Implementation struct obj struct int struct obj struct struct obC:struct int }. and the name of the file from which the class description was read. The first represents the names of the instance variables defined by the class (and. As we noted in the last chapter. each method is represented internally by an array of two elements. so to find the method associated with a particular message you first look up the message name in the message names array and then. representing. Thus for a class description such as the following: I abc I [ Class test1 :Test2 first: x . respectively. They give the maximum size of the context and the stack needed to respond to any message defined in the class. context size and stack max. four primitives are important. In particular. The second contains the names of the messages to which the class will respond. the c size field is always a designated negative integer. Primitive number 112 assigns a value to a given position in an array. This array runs in parallel with the message names arrays. *message names. each instance of class Interpreter independently maintains its own stack of intermediate values. The class_ name. The final two fields. *c_inst_vars. For this is important for bootstrapping purposes because the first classes created cannot have access to any methods defined in other classes. extract the associated method. the name of the super class. by the size of this array. stack_max. are used in constructing interpreters to respond to messages accepted by the class. the value of which indicates that this is an object of class Class. As the next chapter will describe in more detail. Primitives 97 and 98 create new instances of class Class and insert them in the class dictionary. the name of the class. Primitive number 110 creates an array of a specified size. The c inst vars and message names fields are both pointers to arrays of symbolS. *methods. Instances of class Array and class Class can be created by using primitive operations. The methods field is a pointer to an array containing the internal representation of the method associated with each message.

temp +.33 Ii I The following Little Smalltalk statements are generated.< primitive 110 2 > <primitive 112 temp 1 II second II " #( #[ 16 87 194 113 33 48 193 243 245] "#( 33 ) ) > <primitive 112 temp 2 II first: #( #[33 83 192 96 245] "#(» > II "- Primitive number 97 creates a new instance of class Class.Note that no message passing is involved and so these commands can be the very first ones executed by the Little Smalltalk system. t i . these commands will define the class Testl and provide it with the methods given by the class description. The Process Manager 175 a+-x+3 second i+-a*7. initializing it with the values taken from its arguments. as follows: <primitive 98 #Test1 "<primitive 97 #Test1 #Test2 #test1.. First primitive 110 generates an array of sufficient size to hold the methods for the class. Together. Primitive number 98 takes an object of classClass and places it into the internal class dictionary maintained by the Little Smalltalk system.st "#( #a #b #c) "#( #second #first: ) "temp 2 3 > > When read by the Little Smalltalk system. these two primitives can define a new class. . Primitive 112 then places the description of each method into the appropriate location in this array.\.

""""""""""""""""'" ~{{ H{{{{{{{{{{{{H{{{{{{{ ~}}}}}}}}}}}}}}}}}}}}}}}}} ((((((((((((i \)\\)))))))))))))))\))) 176 . 15 The Interpreter »»»»» ////////// ."\ . }}}}}}}}}}}}}}}}}}}}}}}n: «««««««««««(..' ~»»»»» CHAPTER ////////// """"""""""""""""""" {{{{{HH{{{{{{{{{{{{{{{{.

an instance of class Interpreter is constructed. The creator field is usually null. Note that there are several more fields in addition to the ones alluded to in the last paragraph.\ . 1. the interpreter that was active at the point the message was sent which caused the current interpreter to be created. t-size. the creator points to the interpreter in which the block was originally defined.e. there are two objects that can be said to represent the receiver of a message. The currentbyte pointer indicates the next bytecode to be evaluated when execution continues. When a message is sent.. \ ): "\ \ . and the stack to be used by the virtual machine executing the method. Actually both of them. since. * cu rrentbyte . the stacktop is a pointer into one entry of the array stored in the object pointed to by the stack field. and thus the interpreter to be returned from in the case of a block return. as we saw in Chapter 11.1. { t ref count.2). ~ The Interpreter 177 As we have previously noted. * context. *stack. the receiver of the message. the object in which class the method was found) is explicitly pointed to by the receiver field in the interpreter. In this case. In a similar fashion. *literals. In fact it is a pointer into the array associated with the bytecode field (Figure 15.1 D The internal representation for class Interpreter struct interp_struct int int struct struct object object object object object object uchar }./* should always be INTERPSIZE */ interp struct *creator. The sender field points to the sending interpreter. *bytecodes.. except in the case of interpreters created to execute blocks. methods are internally represented by bytecode format (Chapter 13). *receiver. . The actual receiver (i.l the literals and context needed by the method. In Little Smalltalk the named receiver is represented by the first position in the Context array. * * stacktop. that is. The internal C structure for instances of class Interpreter is shown in Figure 15. interp-struct *sender. namely the bytecodes for the method. Figure 15. This object collects the necessary components for executing the method associated with the message.

The procedure cr interpreter( ) creates a new instance of class Interpreter. and places the new interpreter in the front of the sender interpreter in the process queue. .I "" The interface to the interpreter module is shown in Figure 15. it resumes execution from the point it last left off.\ .'i:. The bytecode pointer is set to the first byecode in the method. The procedure is so called because.2 0 CurrentByte points into the byte co de array byteco de cu rrentbyte . and the size of the stack is determined by examining the class of the receiver. Normally the interpreter involved is the sender although in the case of a block returnit will be the sender of the creator. This is accomplished by pushing the response onto the senders stack and then resuming execution of the sender interpreter. The routine copy arguments( ) is used to copy an array of argument values into the context for an interpreter. both of which are passed as arguments. and by the biock execution module to pass arguments to interpreters associated with instances of class Block. 178 The Implementation Figure 15. The routine push object() performs the first of these actions. In the former case the interpreter is taken off the process manager queue and the sender process moves to the top from where it will be subsequently resumed by the process manager. when called by the process manager. It is used both by the courier when the interpreter is first defined. The courier then finds a method to match with a message. The actual interpretation of bytecodes is performed by the process resume( ). When a message returns. Bytecode Array . creates a new instance of class Interpreter. pushing an object onto the staCk of an interpreter.3. It then executes bytecodes until either the method terminates or until a message is sent. In the second case the courier is called. initializing it with the values given by the arguments. the response to the message must be passed back to the sender.

*receiver. and special instructions (opcode 15).context) . The loop reads each bytecode in turn.:l:. The macro nextbyte places the next byte in the argument and advances the currentbyte pointer. and the switch selects which actions to take to execute the opcode. argCount. and loads the arguments into the context for the interpreter at the specified locations . send a message (opcodes 8 through 13). creates a new instance of class Interpreter copy arguments(anlnterpreter. Push Opcodes Writing the code for those actions that manipulate the stack is greatly simplified by a number of macros that select various fields from the interpreter object: . object **argArray. . resumes (or begins) evaluation of the byte co des associated with an instance of class Interpreter The structure of the procedure resume( ) is very regular. anObject) struct interp_struct *anlnterpreter.4)."\ .3 0 The interface to the interpreter module cr interpreter(sender. It is merely a large infinite loop (exited by a return from within the loop) surrounding a switch statement (Figure 15. pushes the object onto the stack associated with the interpreter resume(a nInterpreter) struct interp struct * anInterpreter. push~ object(anlnterpreter. We can divide the bytecodes into groups with similar functions. int argLocation. struct obj struct *anObject. block creation (opcode 14). struct obj struct *Iiterals. receiver. argLocation. argArray) struct interp_struct *anlnterpreter. The Interpreter 179 Figure 15. bitearray. argCount. *context. pop an object from the stack (opcodes 6 and 7).struct interp_struct *sender. These groups are those that push objects on the stack (opcodes 1 to 5). *bitearray. By modular arithmetic the byte is then converted into the high order and low order four-bit portions. literals. takes a pointer to an array of arguments.

} } } # define push(x) {assign(*(anlnterpreter->stacktop). break. switch(highBits) { default: cant_happen(9). x). caseD: actions for opcode 0 break. '" anlnterpreter->stacktop + + . while(l) { nextbyte(h i9hBits). break. lowBits.} # define instvar(x) (anlnterpreter->receiver)->inst var[ x ] # define tempvar(x) (anlnterpreter->context)->inst var[ x] # define lit(x) (an Interpreter->Iiterals)->inst_var[ x] These macros make the code for the first three opcodes trivial: case 1: /*push instance variable */ push(i nstva r(lowBits» .4 0 The structure of the procedure resumer ) resume(a nInterpreter) struct interp struct *anlnterpreter { local dedarations int highBits.\~\. case 2: /* push context value */ push (tempva r(lowBits» . highBits / = 16. - 180 The Implementation Figure 15. lowBits = highBits % 16. . case 15: actions for opcode 15 break.

- The Interpreter 181 case 3: /* push a literal */ push(1 it(lowBits» .\ ):. else if ((IowBits > = 30) && (IowBits < 60» { 1* get class */ tempobj = new sym(c1asspecial[lowBits . or a class and pushes it onto the stack. a pseudo variable. break. Opcode 5 selects either an integer. and the primitive manager is called as for opcode 4. which pushes a class object onto the stack. Each of the pseudo variables has an internal C pointer associated with it. } else tempobj = new int(lowBits). and first the associated class object must be found.= 15) tempobj = (object *) runningProcess. else if (IowBits = =-10) tempobj = new int( -1 ).= 13) tempobj = 0 nil. The routine new_int returns an object of class Integer. is complicated slightly by the fact that the object in the value field is a symbol. case 5: /* special literals */ if (IowBits < 10) tempobj = new int(lowBits).= 12) tempobj = 0 false. sharing multiple copies if the object occurs more than once. tempobj = primitive(FINDCLASS.= 14) tempobj = 0 smalltalk. 1. break. if (! is symbol(tempobj» cant happen(9). A later section will describe the primitive manager in more detail. else if (lowBits = = 11) tempobj = 0 true. push (tempobj). &tempobj). This is achieved by calling the primitive manager with the message FINDCLASS. . Otherwise. break. 1. Opcode 4. push(tempobj) . case /* push class */ tempobj = lit(lowBits). else if (lowBits =.30]). tempobj = primTtive(FINDCLASSS. - \~\. to get a class a new symbol is created. else if (IowBits =. else if (IowBits =. &tempobj). else if (IowBits =.

».>stacktop» This makes Opcodes 6 and 7 easy. therefore. ». ~\ "\ :' \ 182 The Implementation PopOpcodes Like the push opcodes.. the receiver for the message is pushed on the stack. case 6: /* pop and store instance variable */ assig n (i nstva r(lowBits). the stack looks as follows: top of stack argument n argument 1 receiver bottom of stack . Message-Sending Opcodes To send a message will usually require several instructions in bytecode. the writing of the code for the instructions that pop objects from the stack is greatly simplified by first defining useful macros. popstack( break. popstack( break. case 7: /* pop and store in context */ assig n(tempva r(lowBits). followed by the arguments in order.' \. When the send message opcode is read. Finally. in this case a macro to pop an object off the stack and return it: # define popstack() (*(--anlnterpreter. First. a send message instruction is given.\.

the greater the necessity for unconditional jumps becomes.(numargs + 1».call courier to send a message */ do send: receiver = *(anlnterpreter->stacktop . message = symbol value(tempobj). (This is. or from placing the duplicated code in a procedure since the amount of sharing between the interpreter and the procedure would have to be so great. /* do send . is in many ways similar to writing for an actual machine. tempobj = lit(i). The code for opcodes 8 and 9 is shared by first placing the receiver into a local variable and executing an unconditional jump to a common section of code. do send2: .message */ numargs = 10wBits. The closer one gets to an actual machine. The next byte is then a pointer into the literal table where a symbol corresponding to the message is stored. do not have to be described at this level. goto do~send. rece iver = fnd su per(an Interpreter . return. message.decstack(numargs + 1). .) . if (! is symbol(tempobj» cant happen(9). /* send a . numargs). 2. In this case. an Interpreter. in fact.'. goto do_sendi". the low-order bits of the opcode give the number of arguments associated with the message. such as the interpreter. tempobj = lit(i). message = symbol value(tempobj). case 9: /* send a message to super */ numargs = 10wBits. send mess(anlnterpreter. nextbyte(i).> receiver) . The Interpreter 183 In the most general case. Writing a virtual machine. one of those rare occasions when hue block-structured subprocedures would be useful in C since so much information must be shared by the two routines.\. opcodes 8 and 9. (Think how difficult it would be to do assembly language programming without jumps. if (! is symbol(tempobj» cant happen(9). nextbyte(i).>stacktop . fortunately. the sin of the "unstructured" goto seems less serious than the problems that could arise from duplicatirig the code and thereby running the risk of doing two different things where only one is intended. receiver. The use of the goto in this case might be considered with horror by those who do not understand the principles of structured programming. 2 case 8.) Most user programs. and thus the avoidance of gotos usually results in programs that are cleaner and easier to understand.

case 11: /* send a special binary message */ numargs = 1. Opcodes 10 through 13 avoid looking up the message. taking it instead from a built-in table of messages. then the standard calling sequence is followed. instead of having useless references to them left lying around). Opcode 12 could be handled similarly. goto do_send. i = int value(tempobj). The variable receiver contains the receiver for the message. message = binspecial[lowBits]. tempobJ = *(anlnterpreter. if (! is_integer(tempobj)) goto ohwell. (Note this is a pointer to a character sting and not an object. the stack is decremented and pointers in the stack are changed to point to nil. Control is then passed back to the process manager.\ ". by far the greatest number of these messages sent and a sizai:>le percentage of all messages sent. goto do_send. .) The code at label do send: looks into the stack to find the receiver. One completely transparent optimization. goto do_send. if (! is_integer(tempobj)) goto ohwell. case 10: /* send a special unary message */ numargs = 0. The courier is then called via the procedure send mess().1). case 12: /* send a special arithmetic message */ tempobj = *(anlnterpreter . whereas. If not. and a very cost-effective one. therefore. involve arguments that are both integers. (This insures that objects no longer being used are quickly recovered. case 13: /* send a special ternary keyword message */ numargs = 2. When the current process is restarted.2). message = unspecial[lowBits]. The courier creates a new interpreter and places it in front of the current interpreter in the process queue. is to perform these operations in the interpreter if the arguments are both instances of class Integer. message = keyspecial[lowBits]. in the case of opcode 9. the receiver is taken· to be the superobject of the current receiver.>stacktop . a~d the variable message contains the character string representing the message name. The macro decstack( ) merely pops the specified number of locations from the stack. \ ~~ \ 184 The Implementation The local variable numargs holds the number of arguments for the message. Upon returning from the courier.>stacktop . the interpreter given control will be the new one placed in front of the present interpreter by the courier. However.

case 10: i = (i ! = j). break. (i <j) ? i : j. the next byte gives the location in the context where the arguments should be stored when the block is invoked. push(tempobj). If the number is non-zero. send message conventional way */ numargs = 1. message = arithspecial[lowBits]. case 11: i = (i > j). The byte following then gives the size in bytes of the bytecodes containing the statements for the block. " "\ The Interpreter 185 j = int value(tempobj). case 14: i / = j. case 15: i =. break. break. case 3: if (i < 0) i = -i. case 12: i = (i > j).= j. break. break. decstack(2). break. case 1: i . default: cant happen(9). else tempobj = (i ? 0 true: 0 false). break. case 8: i = (i < = j). The . case 6: i I = j. } - if «lowBits < 7) 11 (IowBits > 12» tempobj = new int(i). break. case 16: i = (i < j) ? j : i. else i < < = j. break. break. case 5: i & = j. break. break. case 7: i = (i < j). break. ohwell: /* oh well. break.\ ~\ \~\ ~. break. break. i % = j. case 9: i = (i = = j). break. break. cQse 2: i * = j. switch(lowBits) { case 0: i + = j. case 4: if G < 0) i > > = (-j). the low-order bits of the block creation instruction give the number of arguments to the block. goto do send. - Block Creation In the bytecode format. case 13: i % = j.

Special Instructions The code for opcode 15 is the largest of all the opcode sections because there are so many different cases to be handled. Nonetheless. Opcode (15. case 1: /* duplicate top of stack */ push(*(anlnterpreter->stacktop) . A later section will describe this in more detail. opcode (15. the code is rather tediously simple. arg location» . The two exceptions fo this 'are the code for those instructions that return an object and the code to handle the primitive instructions. and opcode (15. The other two place the object to be returned in the local variable tempobj. The procedure new block() is called to create a block. break. case 14: 1* block creation */ numargs = lowBits.3) returns the object currently on the top of the stack.4) performs a block return (which also takes its argument from the top of the stack).5) returns the receivec Block returns will be described in a later section. /* size of block */ push (new block(a nInterpreter. break. which is then pushed onto the stack. it is sufficient to say that it creates and initializes an instance of class Block.186 The Implementation actual bytecodes for the statements in the block follow immediately the block creation instruction. if (numargs) nextbyte(arg location). The current bytecode pointer is then advanced over the text of the block by using the macro skip( ). In the first.0_ ntl). For now. There are three instructions that return an object. nextbyte(i). . once the macros used in the previous instructions have been defined. skip(i). and then branches to a common return section. case 2: /* pop top of stack */ assig n(* (an Interpreter->stacktop) . numa rgs.1». case 15: /* special bytecodes */ switch(lowBits) { case 0: /* no-op */ break.

\ ~. ». /* skip on false */ nextbyte(i).>stacktop--. } break. case 4: /* block return */ block_retu rn(a nInterpreter. break. - The Interpreter 187 ani nterpreter. skip( . . if (tempobj = = 0_true) { skip(i). case 6: /* skip on true */ nextbyte(i) . tempobj = popstack(). push(o_nil).i ). break. case 9: /* skip backward */ nextbyte(i). if (tempobj = = o_false) { skip(i). goto do_return. case :3: /* retu rn top of stack */ tempobj = popstack(): goto do_return. tempobj = popstack(). case 5: /* self return */ tempobj = tempvar(O). skip(i). break.-}. popstack( return. } break. push(o_nil). case 7. case 10: /* execute a primitive */ nextbyte(numargs). case 8: /* skip forward */ nextbyte(i).

} break.>sender. } break. } . } break. anInterpreter . The Implementation nextbyte(i). break. The sender is then made the first interpreter in . The code at do return first checks to see if there is a sender. If there is. anlnterpreter. tempobj).>stacktop). if (is interpreter(sender» { if (! is driver(sender» push object(sender. _ 188 .else { term inate process(ru nn i ngProcess) . default: cant_happen(9). case 12: /* skip ·on false. the object in tempobj is pushed onto the stack in the sender.>stacktop + + . tempobj = pri mitive(i. and if the senderis not the driver. if (tempobj = = 0 false) { skip(i). push true */ nextbyte(i).\ S. tempobj = popstack(). tempobj = popstack( ). } return. nu ma rgs. /* primitive number */ decstack(numa rgs). /* do return-return from a message */ do return: -sender = anlnterpreter . link to process(sender). anlnterpreter.>stacktop + +. push false */ nextbyte(i). if (tempobj = = 0 true) { skip(i). push(tempobj) . . case 11: /* skip true.

nil is seldom an appropriate value to be used in this circumstance. It then calls the primitive handler.\ The Interpreter 189 the process queue by calling the procedure link to process( ). and a pointer to the location in . the current process is terminated.the stack where the arguments (which have not been overwritten) are to be found. When the process manager again restarts the process. determines to whom it should be sent and how it will be transmitted. Calling the process manager. . - The Courier The courier is so called because it carries a message. The interface to the courier is through the procedure send mess(). first decrements the stack over the arguments. The pseudo variable nil is then pushed onto the stack of the calling interpreter. . the new interpreter will be resumed. which immediately returns back to the process manager. and such errors have an annoying way of cascading. If it finds a class that will respond to the message.The code to execute a primitive (case 10). which must be the start of the interpreter chain. The courier afterwards returns to the interpreter. Any operation that cannot be specified in Smalltalk. In each class it looks at the list of messages to which the class will respond. examining the classes of each object in tum. it produces an error message and a trace of the previous messages sent in the current process. passing it the primitive number. If the courier cannot find a class that will respond to the message. the courier walks up the super-object chain of the receiver. searching for one that will match the message being sent. If there is no sender. = The Primitive Handler The primitive handler is the interface between the Smalltalk world and the world of the underlying virtual machine. it creates a context for the message (the size of the context can be determined by the class description) and an interpreter for the message. Once called. such as adding two floating point values together. number of arguments. Unfortunately. This trace is easily constructed by following back the interpreter chain from the current interpreter back to an interpreter with no sender. but does not itself read the messages. which is then restarted. which we have already described. the new interpreter is then linked to the head of the interpreter chain for the currently running process.

(It is already the largest module in the Little Smalltalk system) Appendix 4 will show that primitive operations seem to be collected in groups of ten. Dividing the primitive number by ten will tell which group the primitive falls into. however. In principle only the primitive handler has detailed knowledge of the internal representations of special objects and can manipulate the various fields in these objects. Each of the individual code sections for the various primitive operations ends by an unconditional jump to a return section of the appropriate type. introduced in the discussion of opcode 14. or converting an integer into a floating point value. and so on. must ultimately be performed via the primitive handler. the primitive handler will return an object.). For example binary integer operations have numbers between 10and 29. Float. In practice this is not quite hue. Instances of Block are special objects with an internal structure as shown in Figure 15. From this information you can check type to insure the arguments to the primitive are correct (for example. Thus the start of the primitive handler is a large switch statement to perform type checking. the integer values from instances of class Integer) can be placed into local variables within the primitive handler. The interpreter field in each block points to a copy of the interpreter in which it was created.5) is rather complicated but very regular. creates a new instance of class Block. a several-page switch statement is used to find the appropriate action for each primitive type. and logically can be considered to be part of the primitive handler module. Blocks The routine new block(). . This object can be of any type (Symbol. etc. '\ ~\ . as the memory manager must also know about the internal structure of all objects. String. unary integer operations have numbers between 30 and 39.\ '\ ':. This complex structure is dictated by the necessity to combine some common operations to reduce the size of the primitive handler as much as possible. After performing the correct actions. Also some complex operations are handled by special routines in the modules for different types of objects. Once type checking has been performed. that character primitives are indeed presented with character arguments) can be performed.' 190 The Implementation concatenating a pair of strings. and once more an attempt is made to combine similar actions so as to reduce the necessity for duplicating code. such as symbols or classes.6. character operations numbers between 40 and 49. Also the internal values (for example. 3 The structure of the primitive handler (Figure 15. 3. These are-called by the primitive handler.

The Interpreter 191 Figure 15.\ s.5 D The structure of the primitive handler perform action type check return character return float return symbol . \ ~ '} .

again copies the interpreter for the block. In fact. - The copy shares everything with the original except for the stack and the currentbyte pointer. instances of class Block execute the BlockExecute primitive. In order to perform a block return.. 192 The Implementation Figure 15. will be removed from the interpreter chain.. This instruction. the class parser and the command line driver generate bytecodes so that the same mechanism is used..\ : . the same actions are taken as would if the creator itself were returning the object being returned by the block.6 0 The structure of instances of class Block struct block struct { int b ref count. after verifying that the number of arguments matches the number of arguments defined by the block. int b numargs. int b-arglocation. copies the arguments into the context for the interpreter. If the creator is not found in the interpreter chain (if a block containing a return is placed into a variable or returned from a message and thus outlives its creator). an error message is produced and the value that would have been returned is returned to the sender of the value message which invoked the block. the interpreter chain is examined. searching for the creating interpreter. Returning a value from a block (in the absence of an explicit block return) therefore requires exactly the same sequence of events as a normal return. struct interp struct*b interpreter. and the sender of the creator will move to the top to be resumed next by the process manager. If found. and appends the interpreter to the front of the interpreter chain for the current process. The creator. }. In response to a value message.. and all interpreters following it. int b-size. .

. G. London: MacMillan. Dahl.) [1984] Implementations of PROLOG. (ed.. Birtwistle. Q. the Netherlands: Technological University... B.. Sweden: Studentlitteratur. W. SE-11: 43-59. T. London: Academic Press. 7: 69-88. Budd." IEEE Transactions on Software Engineering. [1981] Turtle Geometry: The Computer as a Medium for Exploring Mathematics. Introduced the notion of structured programming. Includes an article by O-J Dahl on the language Simula.. and Noe.. The concept of Classes was inherited from Simula. E. A. J. A comprehensive description of how the language Simula can be used in producing discrete event models of the type described in Chapter 7. P. [1972] Structured Programming. References Abelson. O. Dijkstra.-J. New York: Wiley & Sons. A special issue of the programming magazine Byte containing a large number of articles on Smalltalk-80 written by members of the Xerox Learning Research Group. Myhrhaug. J. and Hoare. Lund. [1979] DEMOS. Eindhoven. Byte [1981] Special Issue on Smalltalk 6: 14-378. [1985] "The Eden System: A Technical Review. Black. This collection of papers describes many different aspects of the implementation of the language. and Nygaard.. Campbell. A. PROLOG is a language for logic programming. E. Lazowska. K. Birtwistle. A." Computer Languages.-J. Presents an overview of a modem object-oriented operating system. D. M. 193 . T." Technical Report EWD-123. Dahl. M.\.. A. and diSessa. [1973] Simula Begin. D. Simula is a language of the Algol family designed for simulation and is a very important ancestor of Smalltalk. Almes. [1965] "Cooperating Sequential Processes. G. Boston: MIT Press. E. G. [1982] "An Implementation of Generators in C.\.. H. A System for Discrete Event Modelling on Simula. A. Describes how a simple form of generators can be implemented in the language C. Dijkstra.

one of the first in the evolution of Smalltalk languages. The definitive description of Smalltalk.: Addison-Wesley. describes the "Dining Philosophers" problem discussed in Chapter 10. Presents· references to a large number of games and simulation exercises. P. Greenberg. The Smalltalk-80 Programming system developed at Xerox Pare is much more than just the Smalltalk language. [1985] "Seque: A Language for Programming with Streams.. Englewood Cliffs. Snobol4 is one of the earliest attempts at a language for nonnumeric programming. ed. ed. R. and O'Bagy. The language Seque is derived from Icon (Griswold 83) and attempts to deal with sequences as a formal object. Cal. F. [1983] The Icon Progrmnming Language. A. Icon is a language for nonnumerical problems and a descendant of Snobol4 (Griswold 71).. . Contains many extensive examples of simulations and use of the graphics features of the Smalltalk-80 language. G.. and Polonsky. and Kay. Mass.. A.194 References An early paper on sychronization. Xerox PARC Technical Report Describes the language Smalltalk-72... New Jersey: Prentice-Hall. Arizona: The University of Arizona Department of Computer Science.: Prentice-Hall. Goldberg. N. Reading. [1974] Handbook of Gaines and Shnulation Exercise. S. Ghezzi. D. J. J.. Tucson. . [1971] The SNOBOL4 Programming Language. Griswold. Poage. R. and Jazayeri. Beverly Hills. ed. Gibbs. E. Griswold. Inc.. M.: Addison-Wesley. New York: Wiley & Sons. E. [1976] Sl1wlltalk-72 Instruction Manual. [1983] Smalltalk-80: The Language and Its . J. and Griswold. rather than with generators. and Robson. New York: Wiley & Sons. T. E.: Sage Publications. I. [1972] GPSS Primer. Mass.Implementation. Reading. Griswold. [1983] Smalltalk-80: The Interactive Programming Environment. C. Englewood Cliffs.. [1982] Progranlming Language Concepts. This book describes features of the programming environment developed for Smalltalk-80.. A. R. An introduction to the computer simulation language GPSS." TR 85-2. Many of the ideas concerning generators described in Chapter 8 were derived from Icon. Goldberg. A. Describes the features typically found in languages of the ALGOL family and their conventional implementations. Goldberg. M. I.

Reading. ." Proceedings of the Fifth Principles of Programming Languages Sylnposium. Thesis. Actors is a technique for describing object-oriented programming in Lisp. ed. Thomas. September 1984. Describes some early experiments involving teaching Smalltalk to children. Kay." Scientific Alnerican. and Griswold. [1969] "The Reactive Engine" Ph." Technical Report TR-1443. E. C. H. Mass. Ottawa.. operating systems. D. College Park. . [1977] "Microelectronics and the Personal Computer." Comlnunications of the ACM. A good introduction to the philosophy behind the development of the Smalltalk-80 programming system. R. Vol. October 1984. J. D. Reading. Words of Advice. 1. P. [1984] "The Object Model: A Historical Perspective. is taken from SLS. 21: 392-400. Ingalls. A." Proceedings of the 3rd International Joint Conference on. Bishop." Technical Report SCS-TR-64. Kay. A. These three volumes (the first of a planned seven-volume collection) present an extremely complete analysis of most of the important algorithms used in computer science... A collection of papers describing various aspects of the implementation of the Smalltalk-80 system. R.References 195 Hanson. Md. Krasner. and Pugh.. Mass: Addison-Wesley. VoL 2. [1978] "The SLS Procedure Mechanism. Describes the Flex system. R. VoL 3.D. Artificial Intelligence. [1984] "Teaching Fifth Generation Computing: The Importance of Smalltalk. Sorting and Searching. The programming language SLS provides a great deal of flexibility in the area of procedure activation and parameter passing. D. described in Chapter 8. Hewitt. Seminumerical Algorithms. D. University of Utah. Fundamental Algorithms. and Steiger. Includes a lengthy reference list. [1981] The Art of Computer Progrmnlning. Describes how the object-oriented model has influenced machine architecture. an important predecessor of Smalltalk. Knuth. [1983] Smalltalk-BO Bits of History. LaLonde. 237: 230-244. (available on University Microfilms). [1978] "The Smalltalk-76 Programming System: Design and Implementation. L. The concept of filters.: Addison-Wesley. [1973] "A Universal Modular Actor Formalism for Artificial Intelligence... G. A.: The University of Maryland Department of Computer Sciences. Ontario: Carleton University School of Computer Science. R. W. Koved. and language design. R. The language Smalltalk-76 was the immediate predecessor to the language Smalltalk-80 on which Little Smalltalk is based. January 1978: 9-16.

J. N. R." . Scheifler. England: Cambridge University Press. Introduces the language LOGO. Cambridge. T. [1980] Digital Computer Simulation. G. Describes how object-oriented techniques (the Actor model) can be used for computer animation. ed. Simscript. W. A. and Miller. New York: McGraw-Hill. A. Ranweiler. Computers and Powerful Ideas. S. they are considerably different from generators in Little Smalltalk. A good introductory operating systems textbook. Reynolds.. Discusses various solutions to the "Dining Philosophers" problem discussed in Chapter 10.: Hayden Book Company. Describes abstraction techniques for several modern languages.~ . J. C. [1982] "The IBM System/38 Object-Oriented Architecture" in Computer Structures: Principles and Examples. F... C. and Snyder. F. Describes the influence of the object-oriented viewpoint on machine architecture.. Computer Architecture News 8: 4-11.. B.. . Papert.." Proceedings of the IEEE 68: 1119-1130. Rattner. J. Mass. Schaffert. City: Basic Books. CLU is a modern language in the Algol family. [1982] "Computer Animation with Scripts and Actors. and Stephenson.J. Shaw. Although the language includes a concept called generators. Inc. [1980] "The Impact of Abstraction Concerns in Modern Programming Languages. Atkinson. [1980] MindStorms: Children.: Addison-Wesley. Bloom. and Dynamo. M. CSMP. A rather general introduction to computer simulation models illustrated with examples from the languages GPSS. M.. and Cox. [1983] Operating System Concepts. Pinnow. [1981] Alphard: Fonn and Content. J. J. [1981] CLU Reference Manual. Liskov. Maryanski. Shaw.. pp 537-540.\ '. New York: SpringerVerlag. Ord-Smith. Includes a lengthy reference list of associated literature. Moss. K. New York: SpringerVerlag. and Silberschatz. . G." Computer Graphics 16: 289-296. Peterson. J. 196 References Argues that the Smalltalk language will be as important as Prolog in developing fifth-generation computer systems. [1975] Computer Simulation ofContinuous Systems. E. R. K.. R. Describes some of the object-oriented features of a modem processor and its associated operating system. Reading. [1980] "Object-Based Computer Architecture.. Rochelle Park. W.

H.. [1973] "Backtracking in MLISP2. [1974] "HYDRA: The Kernel of a Multiprocessor Operating System. C. and Moon. C. Wulf.References 197 A collection of papers on the language Alphard. Describes a technique for adding the ability to represent objects and message passing to the computer language LISP... A. P. and Pollack. B.. 337-345. E." Contntunications of the ACM. W. a modern language in the Algol family Smith. D. Describes the HYDRA operating system. A. pp. New York: Wiley & Sons A rather theoretical overview of simulation methods. F. . . June 1974. August 1973: 677-685." MIT AI Mento Nwnber 602.. Levin. November 1980.and Enea." Proceedings of the 3rd International Joint Conference on Artificial Intelligence... W. Cohen. Zeigler. Corwin. [1976] Theory of Modelling and Sintulation. Pierson. R. Jones. D.. Weinreb. D. [1980] "Flavors: Message Passing in the Lisp Machine. which is based on objects communicating via messages. K.

which represents a deck of playing cards. ' of ' . One card is dealt from the deck and is not replaced. once a card is dealt from the deck it cannot be dealt again until after the deck has been shuffled again. case: 10 do: [ pri nt ~ 'jack' J .Projects This section contains a series of projects suitable for graduate or advanced undergraduate students in a one-term course based on the material in this book. That is. The order can either be determined a priori in response to this message or produced as each card is dealt out. Some of the projects involve working only in Smalltalk and therefore can be attempted by students with knowledge only of the first part of the book. print ~ print. default: [ print ~ face printString J . Instances of Deck respond to the following messages: shuffle deal The deck of cards is shuffled into random order. 198 . Other projects involve making modifications to the actual implementation and therefore require knowledge of the second half of the book. ( #('hearts' 'clubs' 'diamonds' 'spades') at: suit) t print Implement the class Deck. Card Games Instances of the following class when properly initialized can be used to represent single playing cards from a conventional deck of cards. Class Card I suit face I [ suit: suitValue face: face Value suit ~ suitValue face ~ faceValue printString I print I Switch new: face. case: 1 do: [ print ~ 'ace' J . 1. case: 11 do: [ print ~ 'queen' ] . case: 12 do: [ print ~ 'king' J .

You may wish to add further messages to class Card or to make it a subclass of Magnitude. Instances of class Biglnteger should respond to the following messages: coerce: + printString The argument should be an instance of class Integer. say 1476632. (Volume 2 of (Knuth 81) describes some algorithms that might be useful for this project. Internally. *. For example. ~. integers larger than can be accommodated in the underlying machine representation are encoded as an array of values. it may be preferable to keep the values in reverse order). Polynomials Implement the class Polynomial.) 3. devise a simulation for a simple card game such as Solitaire or Blackjack. depending upon the algorithms selected. If the argument is a BigInteger. the class Biglnteger. Instances of Polynomial represent polynomial values with numerical coefficients. It is suggested that you start with an easy approximation. Later add negative values and other messages. pass the message up to the superclass (Number). A larger value.Projects 199 deal: As many cards as indicated by the argument are dealt out. < = and < = . return a new BigInteger representing the sum. Instances of class Biglnteger represent integers of arbitrary size. (Actually. As with the last project. . produce a class that works only for positive numbers and the message +. If the argument is not a Biglnteger. Similar messages for -. For example. 1 Other messages may be necessary. Instances of Polynomial should respond to the following messages: coerce: Return a new polynomial of degree zero with the argument as coefficient. depending upon your implementation. suppose only values less than 100 could be represented in machine words. Return a string representation of the integer value. Arbitrary Precision Arithmetic Implement the class BigInteger (subclass of Number). Deal returns an array of cards. Using Deck. Return a Biglnteger with the same magnitude. polynomial coefficients are maintained internally by an array of coefficients. could be represented by the array #( 1 47 66 32 ).

Return a string representation of the polynomial value. Other messages may be necessary. <. Return the numerical result produced by evaluating the polynomial on the argument value. return a new Polynomial representing the sum. of course). Return the value of the named coefficient.0. Similar messages for -. the twodimensional array 10 12 6 3 7 14 13 9 21 can be thought of as being composed of the following two vectors shape #( 3 3 ) values #( 10 3 13 12 7 9. Values in the matrix can be defined either by giving them an explicit shape or values array or by modifying a single element. many of the APL operations can be easily simulated in Smalltalk (ignoring syntax.. or others. depending upon the implementation technique. For example. *. that is. If"the argument is not a Polynomial.6 14 21 ) Notice the values are stored in row major.. Define a class for manipulating matrices of arbitrary dimension. pass the message up to the superclass (Number). 4. or ravel order. = and < =. . APL The programming language APL provides a rich repertoire of operations that can be performed on multidimensional arrays. one-dimensional arrays. Because the class Matrix was defined in the last project. matrix multiplication. inner or cross products. 200 Projects deg ree coefficient: eva I: + printString Return the degree of the polynomial. What functionality should matrices exhibit? Possibilities include pointwise multiplication or addition by a scalar or by another matrix. a shape vector giving the dimension of the array and a values vector maintaining the values in the array. Matrices The class Array provides protocol for vectors. If the argument is a Polynomial. Matrices of higher dimension can be thought of as being composed of two vectors.\ . or zero if no coefficient matches the argument.. 5.

Suggest and implement an alternative scheme that permits numerical generality to be assigned at run-time rather than having a builtin hierarchy. Instances of class Fraction would be represented internally by a pair of integers representing a numerator and denominator. A fraction could be produced by the division of two integers. 9.. Lisp 'The class List provides a simple list structure. Numerical Generality One can envision a class Fraction representing rational values. In the case of Fraction. then Float. If you have a terminal with such a capability. Investigate how numerical generality (primitive number 6) is implemented. Operations involving a fraction and a floating point value would produce a floating point value. describe how to modify the class String to respond to the following message aString printAt: aPoint The point is taken to be a pair of coordinates. optionally. then any user-defined classes. and the receiver string should be printed at that location. Show how this class could be modified to accept the LISP-like messages car. Cursor Motions On many terminals the cursor can be moved to an arbitrary location on the screen by sending a special sequence of characters (usually a sequence of unprintable characters).\ ~~ "\ . What other classes would have to be changed? What other Lisp-like features could be simulated? 7.. One difficulty of implementing the class Fraction is the built-in notion of numerical generality. implement the class Fraction. The numerical generality hierarchy is Integer. one would like the generality to come between Integer and Float. 8. cdr. where the number of stars printed is proportional to the value of the given item. It should then produce a graph. Using this new scheme. \~\ \ ~\ Projects 201 6. and cons. Note that a "character" can be constructed for any nonzero value using an integer and the message asCharacter. an array of titles associated with each value. Bar Graphs Instances of the class Bar Graph should respond to messages setting a header and/or a footer and providing an array of values and. . Operations involving two fractions or a fraction and an integer would produce a new fraction. such as that shown below.

. to augment your plot with headers. Set the state of the pen to down. no drawing takes place. You may wish . The direction of the pen is moved the indicated amount. The argument is a Point the pen should move to. Plot. that will produce plots of functions. off the screen. footers.\. the following might be a typical session: graph ~ Plot new graph from: . the name "turtle" describes the drawing instrument in the Abelson system. Pens This project assumes you have access to an output device on which you can draw lines between pairs of coordinates. Set the state of the pen to up. The argument gives the distance the pen should move. Pens can be directed to move either a given distance (go:) or to a specific location (goto:) If the pen is down. Instances of class Pen should respond to the following messages: location up down turn: go: goto: Return the location of the pen as a Point. For example. a pen does not write on the surface as it moves. 202 Projects header title1 title2 titlen value1 value2 value2 ************* ********* ****************** footer 10. a tool for investigating "turtle geometry. Instances of class Plot should respond to either a message giving. (Abelson 81). or other informative indications as in the bar graphs just described. and Gold