0% found this document useful (0 votes)
30 views266 pages

2002 - NN - From Logo To OO - Programming in Squeak

logo programming
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
30 views266 pages

2002 - NN - From Logo To OO - Programming in Squeak

logo programming
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 266

From LOGO to OO: Learning

how to program in Squeak

Draft
Stéphane Ducasse
[email protected]
-2002
To be published by Morgan Kaufman Publishers

June 1, 2002
2
Contents
I Elementary Programming Concepts 9
1 First Contact 11
1 A First Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2 The computer way of looking at the world . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3 Programs, instructions and Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
4 Creating turtles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
5 Tricks of the script editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
6 Things to remember . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
7 Note for the teacher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

2 Of Turtles and Men 23


1 Drawing Line Segments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2 Adding some color . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3 Taking off . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
4 The ABC of drawing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
5 Things to remember . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

3 Taking a Slant 29
1 Right or Left? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
2 The Right Angle of Things . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3 Turtle clock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4 Drawing with angles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
5 Absolute versus Relative Orientation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
6 Regular polygons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
7 Things to Remember . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

4 Looping 37
1 The Shooting Star . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2 Exercising Regular Shapes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
3 Pyramids rediscovered . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
4 Problems to solve . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
5 Things to Remember . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

5 Variables, you said? 45


1 The world of A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
2 Variables to the Rescue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
3 Using Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
4 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
5 Things to remember . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

6 Deeper into Variables 53


1 Further use of variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
2 Naming variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
3 Deep into variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
4 Troubleshooting with Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

-2001 Stéphane Ducasse ([email protected])


4 CONTENTS

5 Regular polygons with fixed sizes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

7 Teaching Turtles 61
1 Script versus Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
2 How to define a method? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
3 What’s in a method? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
4 Art-nouveau break . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
5 Things to remember . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69

8 Composing! 71
1 Example : the square method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
2 New Art . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
3 Squares everywhere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75

9 Arguments 77
1 Argument, you say?! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
2 Arguments and variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
3 Multiple arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
4 Other exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
5 Things to remember . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82

10 Loops and Variables 85


1 A Really Strange Stair . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
2 Tools for Understanding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
3 Practising: Mazes, spirals and other . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
4 Back to reality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
5 For the teacher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93

11 Decomposing to Recompose 95
1 Golden Rectangles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
2 Squares, Pyramids and “Damiers” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
3 Centered Squares . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99

12 Going in circle 101


1 Arc and Circles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
2 Circles and radius . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
3 Do not lose the direction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
4 Looping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
5 Variation on a tear drop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
6 De composition and Composition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
7 Loops and Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
8 Things to remember . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

13 When to stop a loop 111


1 A quick answer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
2 True and false . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
3 Beware of looping forever . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
4 Predicting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
5 Things to remember . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114

14 Finding Information 117


1 Method Finder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
2 Providing multiple argument-results couples. . . . . . . . . . . . . . . . . . . . . . . . . 118
CONTENTS 5

II Objects 121

15 Objects, Messages, and Classes 123


1 Objects: Entities Reacting to Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
2 Classes: Object Factory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
3 What you should have learned . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131

16 Using an Inspector 133


1 Opening an Inspector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
2 Class and Instances . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
3 Getting Information from an Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
4 Changing Object State . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
5 Objects with Implicit Instance Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . 136

17 Browsers: Tools for Reading and Editing Code 139


1 The Micro Browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
2 Browsing one Class: the Class Browser . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
3 The System Browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
4 Defining a Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147

18 Implementing Joe the Box 149


1 Box’s Behavior and State . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
2 Defining the class Box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
3 Initializing Instances . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
4 Accessing Instance Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
5 Drawing a Box and Other Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
6 Limiting Duplication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
7 Looking at Alternate Designs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
8 What you should have learned . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159

19 The Baby Clicking Game 161


1 A Flashing Morph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
2 Defining the class EscapingFlasher . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
3 A Moving Flasher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
4 Interactively Changing the Direction of Flasher . . . . . . . . . . . . . . . . . . . . . . . 164
5 Accelerating Flasher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
6 Handling Keyboard Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168

20 Inheritance 171
1 Inheritance Foundation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
2 The Hierarchy Browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
3 Instance Variable Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
4 Method Inheritance and Method Lookup . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
5 Hiding Superclass Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
6 The Dynamic Aspect of Self Sends . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
7 Extending Ancestor Behavior . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
8 Understanding super . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
9 In Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
10 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185

-2001 Stéphane Ducasse ([email protected])


6 CONTENTS

21 Programming a Simple Morph 187


1 The MiniTurtle class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
2 Initializing a Mini Turtle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
3 Drawing a Mini Turtle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
4 Some more operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
5 Fondamental Aspects of Object-Oriented Programming . . . . . . . . . . . . . . . . . . . 195
6 Experimentations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196

III The world of Bot the Robot 197


22 The World of Bot The Robot 199
1 Basic Tile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
2 Design Discussions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
3 Bot’s World . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
4 First Improvements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
5 Final improvement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203

IV Projects – L-Systems: Fractals and Plant Growth Modeling 205


23 Simple L-Systems and Fractal Production 209
1 L-Systems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
2 Graphical Interpretation of L-Systems . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
3 Enhancing the Turtle class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
4 A Simple Approach . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
5 Testing Some 1-rule based L-Systems . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
6 Analysis of the Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217

24 Advanced L-Systems as Objects 219


1 Modeling L-Systems with Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
2 The Rule class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
3 The L-System class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
4 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
5 L-System containing Subfigures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
6 Other Implementation Strategies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
7 Further Readings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236

25 Modeling Plant Growth with L-Systems 237


1 L-System with Branching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
2 A Turtle with Memory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240
3 Stochastic L-Systems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244

26 Parametric L-Systems 247


1 Parametric L-Systems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
2 Scanning, Representing and Interpreting New Expressions . . . . . . . . . . . . . . . . . 249
3 Parametric Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
4 The ParametricLSystem Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
5 Fun with pL-Systems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
6 Models of Compound Leaves . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
7 Enhancements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
CONTENTS 7

V The Squeak Object model 263

VI Other approaches for teaching 265

-2001 Stéphane Ducasse ([email protected])


8 CONTENTS
Part I

Elementary Programming Concepts

-2001 Stéphane Ducasse ([email protected])


1
First Contact
In this chapter you will begin your first experiences with the Turtle World. You will learn what a turtle
is and how to send it messages that it will execute. After writing your first program, we will explain the
details.

1 A First Program
Before going into any explanation, we suggest you to type the program shown below and in the Figure 1.1
into the editor that you dropped into the working flap. Before typing the program make sure that no other
text is located in the window. Moreover, computers and programming languages cannot deal with even the
most obvious mistakes, so be careful to type the text exactly as it is written above: this means using the
uppercase "T" of Turtle on the second line and terminating each line with a dot . Finally, make sure that all
lines, other than the first one, are terminated by a dot.

Figure 1.1: A first program typed into the script editor.

Next, we want to execute the program, that is the set of instructions you just typed. Click on the Do
it button as shown in Figure 1.2. You should then see the turtle you created show you the result as shown
in Figure 1.3. As you see this is really simple but still constitutes a program. Each green circle represents
a computer turtle, a rather sketchy one, granted! We shall soon learn a little more about what a computer
turtle is.

-2001 Stéphane Ducasse ([email protected])


12 First Contact

Figure 1.2: Executing a first program typed into the script editor.

Figure 1.3: The simple result of a simple program. The turtle you created showed up, then walked, showed
up again, jump, showed up and walked again.

2 The computer way of looking at the world


2.1 Programming languages
Programming consist of elaborating tasks that a computer can perform. However, computers only under-
stand special language named assemblers. Assembler language is a language that manipulate low level
element of computer physical part like chip, register, entry ports.... As writing programs in assembler is
difficult and let to the developer lot of burden, language designers defined and are designing program-
ming languages like Lisp, Prolog, Esterel, C, C++, Pascal, Smalltalk, Java that can be be translated into
assembler or another representation that can be understood by the computer.
The idea behind a programming language is to help the developer while he is programming. Help
means various things like ease of expression of the task, efficient execution of the program, fiability of the
resulting application, proof that the program is correct, readibility of the code written, ease to change the
application later on. There are no per se one best language to solve all the tasks. Programming languages
propose different expressive power.

2.2 Smalltalk and Squeak


Squeak is the name for a programming environment. A programming environment is a set of tools that
programmers use to develop applications. Squeak contains a lot of tools: text editors, code browsers, a
3. Programs, instructions and Messages 13

Figure 1.4: Two examples of error messages

compiler, widgets.... Moreover, Squeak contains libraries of functionalities. For example, you can program
music, animate flash file, access network, display 3D objects...[?]. The difference between librairies and
the tools is quite blurry and not really important in Squeak. Squeak programmers develop their applications
by writing programs using a programming language called Smalltalk . The important aspect of Squeak and
all the Smalltalk based environments is that they are themselves also completely written in Smalltalk. This
implies that if you understand Smalltalk you can have a lot of power at hand.
In this book you will learn how to program in Smalltalk in the context of the Squeak environment. We
chose Smalltalk because we like its simplicity and uniformity but also because Smalltalk that was originally
designed to help novices to program [?]. Note that Smalltalk is used by professional programmers to
develop complex application like UPS tracking parcel system, insurance, banking systems...

2.3 Talking too computers is not always simple


Compared to people, computers have a strange relation to languages. If you ask a friend "Please, bring me
the bread book", most likely your friend will give you the red book which was lying on the table nearby.
A computer, however, would react differently: it would tell you that there is no book made of bread,
and will not think of correcting the obvious mistake you made in the question. Computers are capable of
making highly complicate calculations at incredible speed, but they are not intelligent and totally lack of
imagination. In particular they lack the wits to correct small mistakes. This means that each message given
to a computer must be given without mistake.
The smallest mistakes — even an lowercase letter instead of an uppercase one — is likely to be mis-
understood by a computer. In that case, an error message will appear on the screen. This is likely to occur
when you are attempting you first experiments. So do not despair and try again when this happens. We
have been through this too.
The Squeak environment tries to help you by notifying you when it encounters error in the code you
wrote. Figure 2.3 shows examples of such error messages. These error messages are in fact menus. The
top part of the menu window contains a short description of the error; then, depending on the type of error
some suggested corrections may be listed as options. Until you feel more familiar with Squeak, the easiest
way to deal with an error message is to select the option cancel, which is present in all error messages.
After dealing with the error message, you must find the place that the computer did not understand, correct
it, and retry the messages.

3 Programs, instructions and Messages


3.1 Looking Back at Typing and Executing Programs
Let us first look at the way you wrote your first program. You typed a text, a sequence of instructions, then
you asked the Squeak environment to execute it by pressing the Do it button. You could also have used the
menu associated with the Script Editor or using the short cut Alt-D on PC and command-D on Macintosh
as shown by the figure 1.5. The sequences of instructions has been then executed by Squeak i.e., the textual

-2001 Stéphane Ducasse ([email protected])


14 First Contact

Figure 1.5: Selecting and invoking do it explicitly.

representation of your program has been transformed into a form that is understandable by the computer
and a turtle has been created and executing the messages sent to it.

Hints: If you have to select all the text, you can just click at its beginning (before the first character) or
at its end or on the line after the last expression. If you want to select a word, you can just click anywhere
on the word. If you want to select a line just click at the beginning (before the first character) or end (after
the last character) of the line.

3.2 Analyzing our First Program


A program consists of a sequence of instructions that will be executed by the Squeak environment. In this
book, we call such a sequence a script.

Important!
Vocabulary Point. We called a script a sequence of instructions.

Script 1.1

| caro |
caro := Turtle new.
caro show.
caro go: 100.
caro show.
caro jump: 100.
caro show.
caro go: 100.
Now comes the time to understand the instructions of our first drawing script. In a nutshell, Script 1.1
creates a Turtle named Carolyn, caro for short, and instructs it to show itself and move on different
locations on the screen. Now let’s analyze each line in details. Note however that certain aspects like
variables or classes will be treated in separate chapters.
| caro | This first line warns the compiler (the application that translate the textual description of a
program in a computer understandable form) that we want to use the name caro for something.
Think of it as saying to a friend, from now on I will use the word bungaloo in my sentences.
3. Programs, instructions and Messages 15

caro := Turtle new. This line creates a turtle and associates it with the name caro, a variable,
that we declared before. Turtle requires an uppercase letter T because it acts as a factory of turtles.

caro show. Smalltalk, the language we are using, is an object-oriented language. This means that
objects are created — virtual objects within the computer, of course — and that these objects are
able to understand commands. So, the line can be understood as follow: "Carolyn, show yourself on
the screen".

caro go: 100. This line asks caro to move by 100 units of length on the screen.

caro jump: 100 This line asks caro to jump by 100 units of length on the screen. Yes, Virginia!
computer turtles can jump. Wow!

The other lines are just repetitions of the previous ones, so they have the same effect.

Important!
Any command terminated by a semicolon indicates that this command
needs additional information, such as a length for example.

About Pixels. On a computer screen the unit is called a pixel . A pixel is the size of the smallest point,
which can be drawn on a computer screen. Depending on the type of computer you are using the actual
size of a pixel can vary. You can see pixels by looking at the letters through a magnifying glass.

3.3 Instructions, Messages and Methods


Before going further into the explanation we should make some vocabulary point clear.

Instruction. We call an instruction any syntactically elements. Example of instructions are:

◦ | caro | is an instruction that declares a variable (see Chapter 5).


◦ caro := Turtle new is an assignment that assign a value to a variable (see Chapter 5).
◦ caro go: 100 is a message.
◦ 100 + 200 is a message.

Message. We call a message , a triplet composed by a message receiver , a method name also called a
selector and message arguments possibly empty. Example of messages are:

◦ caro show is a message composed by a receiver, a turtle, and the method selector show.
◦ caro go: 100 is composed by a receiver, a turtle, the method selector go: and an argument the
number 100. Here, 100 represents the length in pixels of which the turtle should move. Note that the
semicolon is part of the method name.
◦ caro turnLeft: 90 radius: 100 is a message composed by a receiver, a turtle, the
method selector turnLeft::radius: and two arguments 90 and 100 (see Chapter 12 and Chap-
ter 9).
◦ 4 timesRepeat: [ caro go: 100 ] is a composed by a receiver, the number 4, the
method selector timesRepeat: and the argument [ caro go: 100 ] a block (see Sec-
tion ??).
◦ 100 + 200 is composed by a receiver, the number 100, the method selector +, and an argument,
the number 200.

-2001 Stéphane Ducasse ([email protected])


16 First Contact

Method. The concept of method will be introduced in the chapter 7. For now consider a method as an
action that the turtle has to perform. Examples of methods are: show, go:, +....

Message separation. You have noticed that each line of the script, except the first one, is terminated by
a dot. As we have said earlier, the first line is not a message. Such a line is called a variable declaration
in computer jargon. Thus, we can make the following observation: each message must be terminated by a
dot, just like the sentences of written English.

Important!
Messages should be separated by a dot.

Hints. A dot . is a message separator so there is no need to put one if there is no message after it like at
the end of a script.

3.4 Variation on a script


Execute the script contained in the Turtle Director window by selecting only the first two lines, then the
first three lines and so on and using the menu or Alt-D command. Try to understand what happens by
yourself before reading further. You can also change the numbers used.
If the first two lines are selected, nothing happens. The turtle named caro is created, but it does
nothing. If the first three lines are selected, caro shows itself at the location of its creation. If the first four
lines are selected, caro shows itself at the location of its creation and then moves. However, since we did
not ask it to show itself on the screen, the result is the same as when only three lines were selected.

4 Creating turtles
We have mentioned that the second line of Script 1.1 is creating a turtle, not the turtle. Indeed, we can
create as turtles as we want. Here is a new script in which two turtles are created.
Script 1.2

| caro marge |
caro := Turtle new.
marge := Turtle new.
caro turtleColor: Color yellow.
caro show.
marge jump: 100.
marge show.
The second line creates a turtle named caro as in Script 1.1. The third line creates a new turtle named
marge. Both turtles are created at the same location. In line four, we ask caro to show change its color so
that we can distinguish the two turtles.
Smalltalk is an object programming language. This means that we can created objects and interact
with them, or that objects can create other objects and communicate with them. Moreover, in Smalltalk,
there are special objects, called classes , which are used to create objects. To understand what classes are
imagine that a class is a factory. A factory of tin boxes creates many boxes of the same size and shape.
After being created, some box can be filled, others can be crushed. When one box is crushed, the other
boxes are not crushed. The same things happens with object created inside Squeak. In our case, caro did
not move, but marge did.
Class names always begins with an uppercase letter. That’s why the name of the turtle class is Turtle
with an uppercase "T". Color is also a class producing color objects. You can think of a class as a
4. Creating turtles 17

factory able to produce unlimited supplies of objects of the same type. Once produced, each object exist
independently from the other. We will go deeper in that topic in the Chapter 15.
Sending the command new to classes creates an object described by its class. Sending the message
new to the Turtle class creates a turtle.

Important!
A class acts as a a factory of objects. Class names always start with an
uppercase.

4.1 Results and Effects: Two different Concepts


The English word computer means someone or something which performs computations, that is, operations
with numbers. In fact, this is what a computer really is: a machine able to perform computations.
Since a computer is good at computing, we want to show you how simple computations can be cal-
culated using Squeak. For each step described below the corresponding contents of the Turtle Director
window are shown in Figure 1.6. You can always look at the window if the explanation is not clear enough.
Begin by typing the text "1+2 " into the Script Editor window. Just typing it does not force Squeak to
execute it. Now if you pressed the Do it button, nothing happens. In fact, 1+2 is executed, this means that
a message with the selector + is sent to the number 1, with the argument 2. So the result 3 is produced.
However, as we did not ask to see the result, it was not printed. For the code manipulating turtle this
was different because we were not interested by the result returned by a message but by the effects of the
message on the turtle. This can be confusing so we will come back later on this difference in chapter ??.
So if you want to know the value of an expression you have to ask explicitly to execute it and to print its
result. This is done by selecting the option Print It of the menu. It instructs Squeak to execute the
selected text and print the result next to it.
As a result, Squeak prints the result, 3, following the text you selected.

4.2 More operations


You can try to perform other operations. Here are a few example of operations:

◦ Subtraction: 10 - 7
◦ Multiplication: 3 * 5
◦ Division: 42 / 7

Feel free to try several operations of your own invention. While experimenting you may think that
Squeak produces wrong results. For example evaluating 3 + 2 * 5 does not return 23 but 25. This is
due to the fact that Smalltalk does not have the concept of mathematical precedence. In Smaltalk + or *
are just method selectors like any other requiring no special treatment. When multiple arithmetic messages
follow each other they are evaluated from left to right. So you have to express the mathematical precedence
yourself by typing 3 + (2 * 5).
The simplicity of Smalltalk comes at a the price that you have to pay attention to this kind of unusual
behavior. You can refer to the Chapter ?? to have a precise description on how messages are composed in
Smalltalk.
You might be surprised by the result of a division, whose result is not an integer. Squeak keeps the
result as a fraction. We shall not go into details about this, but let us just say that it is a really useful
feature because we can perform exact computation. For example, printing (1/3) + (1/3) + (1/3)
displays 1

-2001 Stéphane Ducasse ([email protected])


18 First Contact

Figure 1.6: Using the environment to print the result of a message


5. Tricks of the script editor 19

Figure 1.7: Using Cmd-E short cut of the Script Editor. Cmd-E shows some handy code snippets.

5 Tricks of the script editor


The Script Editor possesses some handy shortcuts to help you typing your scripts. These shortcuts are
illustrated by the Figures 1.7 and fig:tricks2).

1. First by pressing Command-E you can insert some code snippets in the edited text.
2. Then by pressing command-R you can have a menu containing all the predefined methods of the
Turtle class.
3. Finally pressing Command-T just after typing the beginning of a word show you all the methods of
the Turtle class, even the ones you defined.

6 Things to remember
1. Executing an expression can be done by selecting a text representing one or several instructions and
selecting the menu option DoIt from the execution menu.
2. Printing an expression can be done by selecting a text representing one or several instructions and
selecting the menu option PrintIt from the execution menu. Printing executes and displays the
results returned by the execution.
3. A script is a set of instructions which can be executed.
4. A message is composed by a receiver, a selector and some arguments.
5. Messages are separated by a dot.
6. A class is a factory of objects and starts with an uppercase letter.

7 Note for the teacher


We took the decision to introduce object-oriented and especially Smalltalk terminology for example by
using the term “method”, even if the learner would not program using objects until the second part of the
book. We made this choice to avoid to have multiple terms. As a consequence, some terms represent
slightly different concepts. In the first part, the methods represent procedures and to not include polymor-
phic semantics.

-2001 Stéphane Ducasse ([email protected])


20 First Contact

Figure 1.8: Using Cmd-R and Cmd-T short cut of the Script Editor. Cmd-R proposes the list of predefined
methods that can be sent to a turtle. Cmd-R proposes the list of all the methods defined for a turtle starting
with the text you typed.
7. Note for the teacher 21

If your intention is to learn object-oriented programming or to explain it to novices, pay attention to


use the term a turtle and not the turtle. In object-oriented programming, several turtle can be created each
descroibed by the same class but being able to have a specific state.
The difference between the do it and print it actions is important but not crucial for the beginnning.
The first one ask to perform a given action. The second one asks to perform a given action and prints the
result of the action.

-2001 Stéphane Ducasse ([email protected])


22 First Contact
2
Of Turtles and Men

For Reviewers: Is the semantics of the method show clear? Should it be


renamed leaveTrace?or something like that.

The computer turtle used in the preceding chapter is our main tool to create drawings. We think and
hope that, by generating intricate and/or beautiful drawings, you will be enticed to explore how to program
a computer. All drawings are created in a similar way: some turtles are created and you will instruct them
to draw the picture for you. In short, you will become a tamer of computer turtles. This chapter introduces
you with the major principles needed to produce drawing with tamed turtle.

1 Drawing Line Segments


Asking to a turtle to draw a line is rather simple as illustrated by the following script.

Script 2.1 (A simple line)


| caro |
caro := Turtle new.
caro go: 100.

The message caro go: 100 tells the turtle to move ahead while leaving a trace whenever it is
moving. However, unless you are an expert Chinese or Japanese caligrapher, you need to lift the brush
from time to time when you are making a drawing. You will have to learn how to combine the two methods
jump: and go: in your future scripts. Here is a script which draws two line segments separated by a
short space.
Script 2.2 (Two lines)
| caro |
caro := Turtle new.
caro go: 30.
caro jump: 30.
caro go: 30.

-2001 Stéphane Ducasse ([email protected])


24 Of Turtles and Men

Experiment 2.1
Experiment by changing the values in the previous script.

Experiment 2.2
Generate a script able to draw an SOS message using
Morse code. In Morse, an "S" is represented by 3
short lines and an "O" is represented by three long
lines as shown by the figure on the right.

2 Adding some color


So far the line left by our turtle was black. A computer turtle, however, can change the color of its trace
by invoking the method color: with a color. As mentioned already, Squeak is an environment using
objects: programming in Squeak amounts to create objects and send them messages. In particular a color
is an object. One of the way to create a color is Color blue. Color is a class since1 its name begins
with an uppercase letter. Here is the complete script used to draw a blue line.

Script 2.3 (A blue line)


| caro |
caro := Turtle new.
caro color: Color blue.
caro go: 100.

Changing the color of the turtle itself is also possible using the method turtleColor:. The Script 2.4
asked caro to show itself, then to change its color, to go forward, to change its color and then to show
itself another times. So there is only one turtle, caro.
Script 2.4 (A yellow turtle)
| caro |
caro := Turtle new.
caro show.
caro turtleColor: Color yellow.
caro go: 100.
caro show
Colors can be created by names such as blue, red, green... To have the complete list of available names.
Print the expression Color colorNames. You can also create a color using the class method r: red
g: green b: blue where red, green, blue are value between 0 and 1.0. To help you to find a color
of your choice, you can use the expression Color fromUser. You can also use the class method h:
hue s: saturation v: brightness. Color h: 0 s: 1 v: 1 creates a pure red.

3 Taking off
So far so good, but the drawings so far have been a little limited, don’t you think? Drawing a line from
left to right using on and off mode, even using different colors, is not really funny. In this section you will
learn how to change the direction along which the turtle is moving.
A turtle can orient itself along the four directions: east, west, north, and south. These directions are
like those on a map: east is pointing to the left, west to the left, north up, and south down. The method to
give the turtle to orient itself along a direction is simply the name of the direction. So, to ask caro to face
1 People knowing more about Smalltalk might disagree. There are other entities whose name begins with an upper case letter, but

we have not discussed them yet.


3. Taking off 25

south, one simply types caro south. Easy! Here is a script showing what happens to the turtle after it
has been oriented.

Script 2.5 (A gaggle of turtles)


| caro marge lea faye |
caro := Turtle new.
caro go: 100.
caro show.
marge := Turtle new.
marge north.
marge turtleColor: Color yellow.
marge go: 100.
marge show.
lea := Turtle new.
lea west.
lea turtleColor: Color red.
lea go: 100.
lea show.
faye := Turtle new.
faye south.
faye turtleColor: Color blue.
faye go: 100.
faye show.

In this script, we use four different turtles. Each of them, except caro, is oriented toward a different
direction before moving. At the end of the move, each turtle is asked to show itself. This way, you can see
that it is indeed pointing in the direction it was given before the move.

Now we can use the orientation methods to make more complex drawings. Let us start with a square,
for example. Here is a script to do it:

Script 2.6 (A first square)


| caro |
caro := Turtle new.
caro go: 100.
caro north.
caro go: 100.
caro west.
caro go: 100.
caro south.
caro go: 100

Experiment 2.3
Change the size of the square

However, we are not limited to squares. One can do a wide range of geometrical figures as long as
the figure is made of horizontal and vertical line segments only. For example, here is a drawing of a small
stairs.

-2001 Stéphane Ducasse ([email protected])


26 Of Turtles and Men

Script 2.7 (Small stairs)


| caro |
caro := Turtle new.
caro north.
caro go: 25.
caro east.
caro go: 25.
caro north.
caro go: 25.
caro east.
caro go: 25.
caro north.
caro go: 25.
caro east.
caro go: 25.
caro north.
caro go: 25.
caro east.
caro go: 25.
caro south.
caro go: 100.
caro west.
caro go: 100.

Experiment 2.4
Change the size of the stair.

Experiment 2.5
Now, we are ready to draw a schematic side view of the pyramid of
Saqqarah, built in 2500BC by Imhotep, the first known architect. Gen-
erate a script able to draw a side view of the pyramid of Saqqarah. This
pyramid has four terrasses and its top is twice as large than the terrasses as
shown in the drawing on the right.

Experiment 2.6 (Art Nouveau)


Draw the picture shown on the right.

4 The ABC of drawing

Even though the control of the direction of the line segments is somewhat limited, we can even start to
teach caro how to write letters. Here is a script to draw an "A":
4. The ABC of drawing 27

Script 2.8 (The letter A)


| caro |
caro := Turtle new.
caro north.
caro go: 100.
caro east.
caro go: 100.
caro south.
caro go: 100.
caro north.
caro go: 50.
caro west.
caro go: 100
Drawing a letter "C" is no more difficult. We can then attempt to write a script teaching caro to write
the first two letters of its name. Here it is:
Script 2.9 (The beginning of caro)
| caro |
caro := Turtle new.
caro west.
caro jump: 30.
caro go: 100.
caro north.
caro go: 100.
caro east.
caro go: 100.
caro south.
caro jump: 100.
caro east.
caro jump: 30.
caro north.
caro go: 100.
caro east.
caro go: 100.
caro south.
caro go: 100.
caro north.
caro go: 50.
caro west.
caro go: 100.
As we do not want to have a line between the "C" and the "A" we use jump:. You may also have
noticed that we wrote the "C" from right to left just to use as few messages as possible. Now, try to
complete this script to write caro.

Experiment 2.7
Draw the name of caro as shown on the right.

Some remarks. Some purists among you may think that the scripts 2.8 and 2.9 contains a small mistake.
Indeed, the bottom half of the left bar of the "A" is drawn twice since the turtle is going twice over this
segment — once going south, once going north. This is OK since caro is using virtual paint to draw its

-2001 Stéphane Ducasse ([email protected])


28 Of Turtles and Men

trace there is no risk of smearing the previously painted line. So, it is not a mistake as one can see from the
result.
Knowing how to write the best program is a difficult question, lot of issues like the speed, the readibility
of the code have to be answered and depending of the language, the methods used,..., so there is no simple
answer. However, if you ask yourself such a question chose the simplest one. Then if you have problem
after because you program is too slow you can always speed it up.

5 Things to remember
Anatomy of a computer turtle
A computer turtle is an object living within the Squeak environment. It is defined by a position, a direction,
a color and the color of its trace.
◦ The color of a turtle trace describes the color with which a turtle can draw line. A color is created by
sending a message to the class Color.
◦ The turtle color is the color of the turtle itself.
◦ The direction describes in which direction the turtle is heading. This is in this direction that the turtle
will move when it is asked to move forward.
◦ The position describes where the turtle is located at a given instant. All newly created turtles are all
located at the same position.

Table 2.1: Methods introduced in Chapter 2


Method Description Example
Turtle new Creates a computer turtle caro := Turtle new
| x y | Declares variables used in the script | caro |
show Displays the shape of the computer caro show
turtle at its current location
jump: Asks the turtle to move forward by caro jump: 10
a given number of pixels without
leaving a trace
go: Asks the turtle to move forward caro go: 10
by a given number of pixels while
leaving a trace
north Points the turtle direction to the caro north
north, that is the top of the screen
east Points the turtle direction to the caro east
east, that is the right of the screen
south Points the turtle direction to the caro south
south, that is the bottom of the
screen
west Points the turtle direction to the caro west
west, that is the left of the screen
Color xxx Create a color Color blue
color: defines the color of the trace left by caro color: Color blue
the turtle on the screen
turtleColor: defines the turtle color caro turtleColor: Color red
3
Taking a Slant

By now you should be getting tired of drawing figures with only vertical and horizontal lines. In this
chapter you will learn how to change the direction in which a turtle move and draw a line in any direction.
We use the mathematical concept of angle to indicate to a turtle how far it should turn. If you know very
well what an angle is and how to measure angles in degree, you may skip Section 2 and proceed readily
with the examples and exercises of Section 4.

1 Right or Left?

To orient a turtle along any direction one uses the two methods turnLeft: and turnRight: to ask
a turtle to turn to the left or the right. As the semicolon at the end of the names indicates, both methods
are expecting a parameter. This parameter is the angle between the heading of the turtle before and after
issuing the message. This angle is given in degree.

1.1 Let you practice

Experiment 3.1
Try to guess what the created turtle will do. Experiment with the following scripts. Change the angle values
for example to determine what is the angle so that the turtle turns from a quarter, an half or a complete turn.

If you have problem with the notion of angle read carefully the Section 2 before continuing.
-2001 Stéphane Ducasse ([email protected])
30 Taking a Slant

Script 3.1 (Mystère 1)

| caro |
caro := Turtle new.
caro show.
caro go: 100.
caro turnLeft: 45.
caro show.
caro go: 50.
caro turnLeft: 45.
caro show.
caro go: 100

Script 3.2 (Mystère 2)

| caro |
caro := Turtle new.
caro show.
caro go: 100.
caro turnRight: 60.
caro show.
caro go: 100.
caro turnLeft: 60.
caro show.
caro go: 100

2 The Right Angle of Things


As you should know by now, a newly created turtle is pointing east, that is, toward the right hand side of
the screen. If we ask the turtle to turn left by 90 degree, it will end up heading north. If we ask it to turn
right by 90 degree, it will end up heading south. Here is a script illustrating what a turn left by 45 degree is
doing:

Script 3.3 (The meaning of angles)

| caro |
caro := Turtle new.
caro west.
caro go: 100.
caro east.
caro turnLeft: 45.
caro go: 100.

The first part of this script, up to the line caro east, draws an horizontal line to indicate the eastern
direction. The last part draws the line in the direction 45 degrees left of the east direction. You can vary
the value of the angle to see what other values represent. Try the values 60, 120, 180, 240, 360, and 420.
In particular, note that a turn by 180 degree amounts to backtrack on the previous direction.
Do you see any difference between 60 and 420. No. Any two angle values whose difference is 360
or any multiple thereof are equivalent. Try an angle value of 1860 (1860 = 60 + 360 × 5). The result
is the same as that with angle values 60 and 420. 360 degree represents a single turn. As far as orienting
2. The Right Angle of Things 31

oneself, it does not matter whether you add one or more full turns to the orientation. Keep this in mind
when dealing with angles.
Now, let us try the method turnRight:. Here is a script, using two turtles, that you can use to
investigate the correspondence between a left or a right turn.
Script 3.4 (The meaning of angles (2))

| caro marge |
caro := Turtle new.
caro jump: 200.
caro turnLeft: 180.
caro go: 200.
caro turnLeft: 180.
caro color: Color blue.
caro turnLeft: 45.
caro go: 150.
marge := Turtle new.
marge color: Color red.
marge turnRight: 45.
marge go: 100.
In this script, we draw the reference line — that is the line representing the direction of the turtle before
issuing a turn method — using the fact that a turn by 180 degree amounts to go back in the opposite
direction. The reference line is longer that the lines drawn by the turtles. Thus, it will still be visible if the
lines drawn by the turtles fall on top of it. Similarly, the line drawn by caro is longer than the one drawn
by marge. This is similar to the needles of an old time clock.
The reference line, that is the line representing the initial direction, is drawn by caro, but it is common
to both turtles. Indeed, we have seen that all turtles are created equal. In particular, they both point to the
eastern direction initially.

Experiment. Repeat the same series of angle values for both turtles, that is, change the angle value
for the two turn methods. Then, compare the effect of the method turnLeft: 60 (for caro) and
turnRight: 300 (for marge). You can see that turning left by 60 degree is the same as turning right
300 degrees because the sum of the two values is 360 degree, that is a full turn.
Now let us see what happens when turning from another direction. Here is the same script as Script 3.3
but showing the effect of turning from the north direction.
Script 3.5 (The meaning of angles (3))

| caro marge |
caro := Turtle new.
caro north.
caro jump: 200.
caro turnLeft: 180.
caro go: 200.
caro turnLeft: 180.
caro color: Color blue.
caro turnLeft: 45.
caro go: 150.
marge := Turtle new.
marge north.
marge color: Color red.
marge turnRight: 45.
marge go: 100.

-2001 Stéphane Ducasse ([email protected])


32 Taking a Slant

Experiment. Continue to experiment with Script 3.5 by changing the direction of reference. For the
comparison to be meaningful, you must also orient marge to the same direction than caro after creating
it. Try any angle values you like and try to predict what the resulting drawing will look like before executing
the script. Continue experimenting with that script until your predictions are good.
It is important that you can always predict what is going to happen before executing a script because a
computer blindly executes instructions.

3 Turtle clock
We have mentioned that the lines drawn in Script 3.5 were akin to that of an old time watch. The analogy is
more than real. The notion of degree is strongly correlated to that of hours, because the ancient discovered
the notion of time by measuring the angle of the sun (or some star) respective to a direction of reference.
You can study this analogy and modify Script 3.5 as follows:

1. Keep the direction of reference to the north (this is how Script 3.5 is written. The reference line
indicates noon.

2. Use the method turnRight: for both turtles. A turn to the right is what the needles of an old time
clock are doing.

3. You can ask caro to draw the minute needle by multiplying the number of the minutes you want
to indicate by 6. For example, 20 after the hour is shown with the message turnRight: 120
(120 = 6 × 20).

4. You can ask marge to draw the hour needle by multiplying the number of the hours you want
to indicate by 30. For example, 2 o’clock is shown with the message turnRight: 60 (60 =
30 × 2).

Try to indicate a few hours of your choice with that script.

4 Drawing with angles


To begin with, here is a script drawing a triangle:

Script 3.6 (A triangle)

| caro |
caro := Turtle new.
caro go: 100.
caro turnLeft: 120.
caro go: 100.
caro turnLeft: 120.
caro go: 100

Now, you are ready to draw a house.


5. Absolute versus Relative Orientation 33

Experiment 3.2
Draw a house as shown on the right. Hint: the roof is the triangle of
Script 3.6.

Experiment 3.3
Try to draw houses of different shapes.

5 Absolute versus Relative Orientation

Now you should feel confident that you can ask a turtle to execute any drawing made with lines of any sort.
Before going further, have you understood the difference between orienting a turtle — using the methods
north, south, east, and west— and asking the turtle to turn — using the methods turnLeft: and
turnRight:.

First we ask you to complete the following exercise:

Experiment 3.4 (A Relative Square)


Make a script drawing the same square
as Script 2.6 using only the methods
turnLeft: or turnRight:.

Now, add the following line: caro turnLeft: 33. before the first line containing a go: mes-
sage in the script of Exercise 3.4. As you can see one still obtains a square, but it is tilted.

Finally, go back to Script 2.6 and perform the same modification to the script. The modified script is
shown by the Script 3.7.

-2001 Stéphane Ducasse ([email protected])


34 Taking a Slant

Script 3.7 (A Missed Square)

| caro |
caro := Turtle new.
caro turnleft: 33.
caro go: 100.
caro north.
caro go: 100.
caro west.
caro go: 100.
caro south.
caro go: 100.

Do you still obtain a square? No! The first side drawn by the turtle is slanted whereas the other sides
are either horizontal or vertical. What these the scripts Script 3.7 and Script 3.4 show you is the difference
between relative and absolute direction changes.

◦ The methods north, south, east and west change direction in an absolute manner. The direction
to which the turtle will point does not depend on the current direction in which it is pointing.

◦ The methods turnLeft: and turnRight: change direction in a relative manner. The direction
to which the turtle will point depends on its current direction.

6 Regular polygons

A regular polygon is a figure made with line segments of the same length. An equilateral triangle is a
regular polygon with 3 sides; A square is a regular polygon with 4 sides. For example, the Script 3.6 that
draws an equilateral triangle, whose side length is 100 pixel, is obtained by asking caro to go forward a
given distance and to turn 120 degree left or right, the two commands repeated 3 times.

You program a turtle to draw a regular polygon with any number of sides by asking it to go for some
length and then turn left or right by 360 degree divided by the number of sides; this sequence must be
repeated as many times as there are sides.

Experiment 3.5
Try to draw a pentagon, that is a regular polygon with five sides of 100
pixel length.
7. Things to Remember 35

Experiment 3.6
Try to draw a hexagon, that is a regular polygon with six sides of 100 pixel
length.

Maybe, you are just curious to see how far you can go. Using the cut and paste facility of the Turtle
Director window, you can actually generate a regular polygon with a large number of sides. If you are in
the mood, go on increasing the number of sides.

Experiment 3.7
Draw the pace symbol shows by the right figure.

7 Things to Remember
1. A turtle can be oriented relatively to its current direction using the methods turnLeft: and
turnRight:.
2. The parameter given to the methods turnLeft: and turnRight: is given in degree.
3. 360 degree corresponds to a full turn.
4. Angles values whose difference is a multiple of 360 degree are equivalent.

7.1 Commands summary


Here is the list of the commands, which you have learned in this chapter.

Table 3.1: Methods introduced in Chapter 3


Method Description Example
turnLeft: Changes the turtle direction to a caro turnLeft: 30
number of given degrees to the left
turnRight: Changes the turtle direction to a caro turnRight: 30
number of given degrees to the right

-2001 Stéphane Ducasse ([email protected])


36 Taking a Slant
4
Looping

By now you must think that the job of turtle tamer is quite tedious. We are sure that you have ideas of
nice drawings, but you did not get the hart to ask caro to draw them for you. Indeed, the amount of things
to type gets larger and larger as the complexity of the drawing augments. In this chapter, you will learn
how to reduce the amount of commands given to a turtle using loops. Loops allow one to repeat sequence
of messages. With a loop, the script drawing a square is no bigger than the one drawing an hexagon or an
octagon.

1 The Shooting Star


We would like that caro draws a star like the star shown in the picture above. A turtle has to draw a
line, comes back to its previous location, turns from a certain angle and draws another line and so on. The
Script 4.1 makes a turtle drawing a line of 70 pixels and coming back to its previous location. Note that
in addition, after having drawn the line the turtle points in the same direction where it was pointing before
drawing the line.

Script 4.1 (Drawing a line and coming back)

| caro |
caro := Turtle new.
caro go: 70.
caro turnLeft: 180.
caro go: 70.
caro turnLeft: 180.

Now to draw a star, we have to repeat the Script 4.1 and make the turtle turns from a given angle,
for example 60 degree. The Script 4.2 shows how this should be done to obtain a star having 6 branches
without using loops.

-2001 Stéphane Ducasse ([email protected])


38 Looping

Script 4.2 (A star without loop!)

| caro |
caro := Turtle new.
caro go: 70.
caro turnLeft: 180.
caro go: 70.
caro turnLeft: 180.
caro turnLeft: 60.
caro go: 70.
caro turnLeft: 180.
caro go: 70.
caro turnLeft: 180.
caro turnLeft: 60.
caro go: 70.
caro turnLeft: 180.
caro go: 70.
caro turnLeft: 180.
caro turnLeft: 60.
caro go: 70.
caro turnLeft: 180.
caro go: 70.
caro turnLeft: 180.
caro turnLeft: 60.
caro go: 70.
caro turnLeft: 180.
caro go: 70.
caro turnLeft: 180.
caro turnLeft: 60.
caro go: 70.
caro turnLeft: 180.
caro go: 70.
caro turnLeft: 180.
caro turnLeft: 60.

As you see, this is clearly boring to have to type all this code that does all the time the same thing.
Imagine if we would like to have a star with 60 branches like the star shown in the Script 4.4! In fact we
would like to be able to repeat a sequence of messages.

Using a Loop. There is a solution to this problem: using a loop! There are different kinds of loops
like iterating while a certain condition holds or a given number of times. For the moment we can say
that a loop allows one to repeat a given sequence of messages a number of times. In Squeak, the method
timesRepeat: allows one to define a loop that will repeat a sequence of messages a given number of
times like shown in the Script 4.3.
2. Exercising Regular Shapes 39

Script 4.3 (A star with a loop)

| caro |
caro := Turtle new.
6 timesRepeat: [ caro go: 70.
caro turnLeft: 180.
caro go: 70.
caro turnLeft: 180.
caro turnLeft: 60]

The Script 4.3 defines the same star than the Script 4.2 but in a much smaller way.

Deeper into timesRepeat:. The method timesRepeat: allows one to repeat a sequence of mes-
sages a given number of times. The sequence to be repeated is delimited by [ and ], it is called a block.
timesRepeat: is not sent to a turtle but to an integer, the number of times the sequence should be
repeated. In the Script 4.4 the message timesRepeat:: [...] is sent to 60.
Note that the number receiving the message timesRepeat: have to be an integer because as in real
life there is no sense to do a sequence of messages 0.2785 times for example.

Important!
n timesRepeat: [ ...foo...] repeat n times ...foo...

Type the Script 4.3 and change the number of times the loop is repeated by replacing 6 by the number
you want. Pay attention that 60 should be changed accordingly if you want to generate a complete star. To
have a complete star the relation between the angle and the number of repetition should be angle∗n = 360.
In the Script 4.4 the loop is repeated 60 times and the angle is 6 degree, so the star is complete.

Script 4.4 (A star with 60 branches)


| caro |
caro := Turtle new.
60 timesRepeat: [ caro go: 70.
caro turnLeft: 180.
caro go: 70.
caro turnLeft: 180.
caro turnLeft: 6]

2 Exercising Regular Shapes


As you may have noticed, some figures can be obtained by simply repeating sequences of messages, espe-
cially the ones produced in Section 6 of Chapter 3.

-2001 Stéphane Ducasse ([email protected])


40 Looping

Script 4.5 (A first square)

| caro |
caro := Turtle new.
caro go: 100.
caro turnLeft: 90.
caro go: 100.
caro turnLeft: 90.
caro go: 100.
caro turnLeft: 90.
caro go: 100.
caro turnLeft: 90

Experiment 4.1
Granted, that the last turnLeft: message is not really needed, but it does no harm, transform the
Script 4.5 to draw the same square but using the command timesRepeat:.

Now you should be able to draw other regular polygons with a large number of sides.

Experiment 4.2
Draw a pentagon using the method timesRepeat:.

Experiment 4.3
Draw a hexagon using the command timesRepeat:.

If you get the hang of it, try to augment the number of sides of a polygon to a very large number. You
may need to reduce the size of the sides so that the figure fits within the screen. When the number of sides
is large and the size of the sides is small, the polygon looks like a circle.

3 Pyramids rediscovered

Remember how you coded the outline of the pyramid of Saqqarah in Experiment 2.5? You can simplify
your drawing by using a loop as follows:
4. Problems to solve 41

Script 4.6 (Pyramid script)

| caro |
caro := Turtle new.
5 timesRepeat: [ caro north.
caro go: 20.
caro east.
caro go: 20 ].
5 timesRepeat: [ caro go: 20.
caro south.
caro go: 20.
caro east ].
caro west.
caro go: 200.
Now should should be able to generate pyramids with any number of terraces with the same number of
commands, just by changing the numbers of the script.

Experiment 4.4
Try to draw a pyramid with 10 terraces using a variation of Script 4.6.

You may want to generate pyramids with an even larger number of terraces. The size of the terraces
must be adjusted if you want them to fit within the screen.

4 Problems to solve
As you have seen, the generation of the pyramid involves the repetition of a block of code, which draws
two line elements. Once the proper repeating element is identified, one can produce complex picture from
elementary drawing, by repeating themselves. The following exercises illustrate this principle.

Experiment 4.5 (Red Cross)


Draw the outline of the Red Cross using turnLeft: or turnRight:
and timesRepeat:.

Experiment 4.6 (Stair)


Draw the following stair.

-2001 Stéphane Ducasse ([email protected])


42 Looping

Experiment 4.7 (Stylized Stair)


Draw the following stylized stair.

Experiment 4.8 (A Simple Element)


Draw the following graphical element.

Experiment 4.9 (Comb)


Transform the Script 4.8 to pro-
duce a comb.

Experiment 4.10 (Ladder)


Transform the Script 4.8 to produce a ladder.

Experiment 4.11
Now that you master loops define a loop that draws the tumbling squares of the picture shown at the opening
of chapter 3.

5 Things to Remember
5. Things to Remember 43

Table 4.1: Methods introduced in Chapter 4


Method Description Example
n timesRepeat: repeats n times a sequence
[xxx] of messages 10 timesRepeat: [caro go: 10.
caro jump: 10]

-2001 Stéphane Ducasse ([email protected])


44 Looping
5
Variables, you said?

We are constantly giving names to human beings or things. For example, we give names to people, to
dogs or to cars. By doing that we are associating a word or a symbol to something. Once this association
done, we then later use the word or symbol to refer to or to interact with the thing associated with it.
Sometimes, names are for all the life or sometimes just for a short period of time. Sometimes names refer
to other names. For example, an actor can have several names, a public name, a civil name and a name of
the character he is playing in a movie.
In this chapter, we will show that giving names, so called variables in programming languages, to
computer things is also help and is sometimes mandatory for programming1 . We will show why we need
variables. So far the scripts you wrote used values (numbers) which, as the complexity of the scripts
increased, are often correlated to each other. In this chapter we show how to use variables to express
dependencies between numbers.

1 The world of A

As asked in Chapter 2, suppose that we want to use a turtle to write letters. The character A is characterized
by a height, a width and a middle distance from which the middle line of the A should be drawn2 .

1 Some languages do not introduce the notion of variables but they are extremely rare.
2I want to know the technical name of such a distance. Thanks to email it to me.

-2001 Stéphane Ducasse ([email protected])


46 Variables, you said?

Experiment 5.1
Propose a script that draws a character A of 100 pixel height, 70 pixel width
and 60 pixel middle distance.

Variations on A. The script you wrote for answering the Experiment 5.1 should look somehow like the
following script.

Script 5.1 (An A of 100)

| caro |
caro := Turtle new.
caro north.
caro go: 100.
caro east.
caro go: 70.
caro south.
caro go: 100.
caro west.
caro jump: 70.
caro north.
caro go: 60.
caro east.
caro go: 70

Experiment 5.2 (frAnkenstein)


Change the Script 5.1 to draw an A of 200 pixel
height, 100 pixel width and 70 pixel middle distance.

As the previous experiment shows you, every times we want to produce an A of a different size we have
to change everywhere and synchronously the numbers that represent the height, the width and the middle
distance. By synchronously, we mean that 100 should become 200, 70 100 and 60 70 without mixing them.
2. Variables to the Rescue 47

Experiment 5.3
Change the Script 5.1 to draw other A letters with different sizes of your choice. Try to reproduce all the
As that compose the first picture of this chapter.

As you may notice, having to change the values everywhere is boring. Moreover, you also may end up
by being confused and forget to change a value at one place. This would result in breaking the script. You
can easily imagine that in complex programs this could be a real problem to change values in such a way.

2 Variables to the Rescue


As seen with the previous exercises, producing the letter of different size is tiresome and troublesome. We
have to take care to change all the values everywhere. In fact, we would like to be able to

◦ define the height, width and middle distance of an A once for all,
◦ being able to refer to these values, and
◦ possibly change the values if necessary.

This is exactly what a variable allows one to do! Surprising isn’t?

Definition
A variable is a name to which we associate a value. We can then refer
to a variable and obtain the value associated with this variable. It is also
possible to modify the value associated with a variable and associate it
a new value. Note that in Smalltalk, there is no constraint on the value
that can be associated with a variable, it can be a number, a collection of
objects, ... even a turtle.

The following sections illustrate how to declare, associate a value to a variable and use it.

2.1 Declaring a variable


Before using a variable, we have to declare it, i.e., telling to the Squeak system the name of the variable that
we will use. In Smalltalk, declaring a variable is done by enclosing variables between ||. To be exact, ||
declare local variables (Variable names can also contain numbers. For a complete and precise description
read the Chapter ??).

| height width middle |

Explanation: This code defines three variables height, width and middle.

2.2 Assigning a value to a variable


Before using a variable, i.e., being able to refer to the value that is associated with it, we have to associate
it a value. Associating a value is called assigning a value to a variable. In Smalltalk, assigning a value to a
variable is made using := 3 .
3 In Squeak, you can also use the arrow ←. However, it is better to use := because this is standard in all the other Smalltalk and

the arrow is not defined in all the font sizes.

-2001 Stéphane Ducasse ([email protected])


48 Variables, you said?

| height width middle |


height := 100.
width := 70.
middle distance := 60

Explanation: We assign 100 to the variable height, 70 to the variable width and 60 to the variable
middle distance.

Vocabulary. When this is the first time that we assign a value to a variable, we say that we initialize it.

2.3 Using Variables

To use a variable, i.e., to refer to the value assigned to it, you just have to write its name in a script.

| caro height |
caro := Turtle new.
height := 100
caro north.
caro go: height

Explanation: After being declared line 1, the variable height is initialized with the value 100 in line 2
and used line 4 to say to the created turtle to go forward from the height variable’s value, here 100.

2.4 About caro

You guessed right! caro is also a variable, a variable whose value is a turtle. Hence, | caro | declares
that we use a variable named caro. The expression caro := Turtle new. initializes the variable
with a value, here a new turtle. Then we use it by sending it messages, for example caro go: 100.

3 Using Variables

We now explore the benefit of using variables. In particular, we show that linking variables together is
powerful.

3.1 Back to our writing

When we introduce variables in the Script 5.1, we obtain the Script 5.2. Now changing variable values is
easier. Change some values to convince you.
3. Using Variables 49

Script 5.2 (An A of 100)

| caro height width middle distance |


caro := Turtle new.
height := 100.
width := 70.
middle distance := 60.
caro north.
caro go: height.
caro east.
caro go: width.
caro south.
caro go: height.
caro west.
caro jump: width.
caro north.
caro go: dist.
caro east.
caro go: width

3.2 Linking Variables


To be efficiently read or simply nice to look, a letter should respect certain proportions, i.e., the lengths
that described it are not freely chosen but hold certain proportions between them. Let us decide that, for
example, the width should be 2/3 of the height and that the middle distance should be at 3/5 of the height.
We can express these relationships using variables as shown in the following Script 5.3. Indeed the value of
a variable does not have to be limited to a simple number but can be the result of a complex computation.
In the same way, the value of the variable caro is the result of the expression Turtle new that returns
a turtle.
Script 5.3 (An A with linked variables)

| caro height width middle distance |


caro := Turtle new.
height := 120.
width := 120 * 2 / 3.
middle distance := 120 * 3 / 5.
caro north.
caro go: height.
caro east.
caro go: width.
caro south.
caro go: height.
caro west.
caro jump: width.
caro north.
caro go: dist.
caro east.
caro go: width
If you analyze a bit the Script 5.3, you realize that it is not optimal. The relationships are expressed
between the variables, but still the value 120 (lines 3,4 , and 5) has to be changed manually when we
want to produce a different A holding the same relations. The solution is to use the variable height
instead of 120 as shown in the Script 5.4. In this script, the value of the variables height, width and

-2001 Stéphane Ducasse ([email protected])


50 Variables, you said?

middle distance are linked together. As you see the value of a variable can be expressed by using
other variables.

Script 5.4 (An A with well linked variables)

| caro height width middle distance |


caro := Turtle new.
height := 120.
width := height * 2 / 3.
middle distance := height * 3 / 5.
caro north.
...

Important. The only constraint we have while linking variables together is that the value of variable used
in the definition of another should be known. For example, in Script 5.4, the value of height is known
while computing the value of width and middle distance. On the contrary, in Script 5.5 the value
of height is not known when the value of width is computed, this leads to an error. We will elaborate
more on that in the Chapter 6.

Script 5.5 (Wrongly linked variables)

| height width middle distance |


width := height * 2 / 3.
height := 120.
middle distance := height * 3 / 5.

4 Exercises

Experiment 5.4 (Golden Rectangle)


A golden rectangle is a rectangle whose height is 1.6 times its width — 1.6 is an

approximation of the golden number 1+2 5 and it is the side of a regular decagon
inside a circle of unity. The nice property of such a rectangle is that if we draw inside
the rectangle a square with size the rectangle’s width, then the left part of the rectangle
is again a golden rectangle. Such a property is then infinite. Every rectangle created√
this way is a golden rectangle. Define a script that draws a golden rectangle. 1+2 5 is
expressed in Smalltalk as 1 + 5 sqrt / 2.
5. Things to remember 51

Experiment 5.5
Explain why none of the following scripts do not draw an A of 120 pixel height.
| caro height | | caro height |
caro := Turtle new. caro := Turtle new.
height := 120. caro north.
caro north. caro go: height.
caro go: 100. caro east.
caro east. caro go: 70.
caro go: 70. caro south.
caro south. caro go: height.
caro go: 100. caro west.
caro west. caro jump: 70.
caro jump: 70. caro north.
caro north. caro jump: 50.
caro jump: 50. caro east.
caro east. caro go: 70
caro go: 70

Pyramids rediscovered. In Section 3 of Chapter 4 in the Script 4.6 we defined the outline of the pyramid
of Saqqarah the following way.
Script 5.6 (Pyramid script)
| caro |
caro := Turtle new.
5 timesRepeat: [ caro north.
caro go: 20.
caro east.
caro go: 20 ].
5 timesRepeat: [ caro go: 20.
caro south.
caro go: 20.
caro east ].
caro west.
caro go: 200.

Experiment 5.6
Introduce the variable terracesNb that represents the number of terraces that the pyramid can have.

Experiment 5.7
In the previous script introduce the variable terraceSize that represents the size of a terrace.

5 Things to remember
1. A variable is a name to which an object (a number, a turtle, a color...) is associated. We say that a
value is assign to a variable. When this is the first time that a value is assigned to a variable, we say
that the variable is initialized.

2. A variable should be declared and should initialized, before being used.

-2001 Stéphane Ducasse ([email protected])


52 Variables, you said?

3. The variable can be used at any place where its value can be used.

5.1 Commands summary

Expressions Description Example


| hvariable namei | Declaration of a variable | caro height |
hlength i := hexpressioni Assigns the value of expres- length := 40
sion to a variable
length := length + 10
Uses a variable’s value caro go: length
length := length + 10
6
Deeper into Variables
In the previous chapter we introduce variables, in this chapter we want to help you really understanding the
idea behind variables. To that purpose we show you some other examples of variable uses and going a bit
deeper in the explanation of variables.

1 Further use of variables


As you have seen in the previous chapter, the use of variables greatly simplifies the writing of scripts where
some of the variables depend on each other. In this section, we shall see how the use of variables gives a
great leverage when dealing with loops. The Chapter 10 will go deeper in showing that the combinaison of
variables and loops is powerful.
Let us come back a while on Exercises 4.2 and 4.3 in which a turtle was asked to draw a pentagon and
a hexagon. Let us look again at these two scripts.

Script 6.1 (Pentagon)


| caro |
caro := Turtle new.
5 timesRepeat: [ caro go: 100.
caro turnLeft: 72 ]

Script 6.2 (Hexagon)


| caro |
caro := Turtle new.
6 timesRepeat: [ caro go: 100.
caro turnLeft: 60 ]

The difference between the two scripts is that one must change the number of sides (let us call it s) and
the magnitude of the turn (let us call it T ) such that the product s × T is equal to 360. Wouldn’t it be nice
if we could write a script, in which we only would have to change a single number, let us say the number
of sides since this is the easiest parameter to choose? This is possible by introducing variables. Here is a
script where this is done. Try it and we shall discuss more about it.
By changing the "6 " in the 3rd line — that is, the line side := 6 to another number, you can reuse
this script to draw a regular polygon with any number of sides.

-2001 Stéphane Ducasse ([email protected])


54 Deeper into Variables

Script 6.3 (Regular polygon)

| caro side angle |


caro := Turtle new.
side := 6.
angle := 360 / number.
side timesRepeat: [ caro go: 100.
caro turnLeft: angle ]
In Script 6.3 we have introduced two new variables — side and angle — used to hold the needed
values. Then, the line side := 6 assigns the value 6 to the variable side and the line angle :=
360 / side assigns a value to the variable angle, which is the result of 360 divided by the value held
in the variable number. The value of the variable angle is then used as argument of the command
turnLeft: given to the turtle in the repeating block.

2 Naming variables
As we have said earlier, a variable is simply a named place holder to keep an object for further use.
Of course, you can give the name you want to a variable. However, giving meaningful names is really
important because it will help you to program and to read your program. To illustrate this point, read the
Script 6.4 which is in fact the Script 6.3 rewritten using meaningless variable names.

Script 6.4 (Unreadable script)

| x y z|
x := Turtle new.
y := 6.
z := 360 / y.
y timesRepeat: [ x go: 100.
x turnLeft: z].

As you can find out by trying it out, the script above is perfectly understood by Squeak. We leave you
to decide which of the two scripts—6.3 or 6.4—is more understandable.
The name of a variable can be any sequence of alphabetical and numerical characters beginning with
a lower case letter. In Smalltalk, it is customary to use long variable names indicating clearly what the
variable means. This way a script cannot only be understood by Squeak, but, more importantly, it can
be also understood by you or other programmers. This is very important since, as you will see later,
programming involves the combination of many scripts1 . After a while, when you need to understand a
script written by someone else—or by yourself, but many months ago—you will be thankful for this habit
of using meaningful names for variables.

3 Deep into variables


3.1 Experimenting
To better understand how variables are manipulated, we give herefater a series of scripts. We ask you read
the script and guess what we will be result of the script, then to evaluate the script. You can also select all
1 This is another simplification: soon, we shall learn about methods, the real way of programming with objects.
3. Deep into variables 55

the lines except the last and use the Print It menu. to print the value returned by the script. The value of a
script is normally the value of the last expression that composes it.
Script 6.5

| caro size |
caro := Turtle new.
size := 100.
caro go: size

Explanation: size is declared, then 100 is assigned to it, and finally the variable value, here 100, is
used to specify from which distance the turtle should go forward.

Script 6.6

| caro size |
caro := Turtle new.
size := 100.
caro go: size + 100.

Explanation: size is declared, then 100 is assigned to it, and finally the variable value, here 100, is used
and add to 100 to specify from which distance the turtle should go forward. So the turtle goes forward
200 pixels.

Script 6.7

| size caro |
caro := Turtle new.
size := 100.
size := 300.
caro go: size

Explanation: size is declared, then 100 is assigned to it, then 300 is assigned to it and finally the
variable value, here 300, is used. The value of a variable can indeed change with time.

Script 6.8

| size caro |
caro := Turtle new.
size := 100.
size := 200.
size := 100.
caro go: size

Explanation: size value is changed several times. The last one only counts.

Script 6.9

| size caro |
caro := Turtle new.
size := 100.
size := size + 100.
caro go: size

Explanation: size is initialized with 100. Then it refers to value of the expression size + 100. At
this point, size value is 100, so 200 is now assigned to size. So the turtle goes forward 200 pixels.

-2001 Stéphane Ducasse ([email protected])


56 Deeper into Variables

Script 6.10

| size caro |
caro := Turtle new.
size := 100.
size := size + 100.
size := size + 100.
caro go: size
Script 6.11

| size caro |
caro := Turtle new.
size := 100.
size + 200.
caro go: size

Explanation: The Script 6.11 shows that manipulating the value of a variable without reassigning it to
a variable does not change its value. size is initialized with 100. Then the value of size, here 100, is
added to 200. Then size value, here 100, is used to specify how far the turtle should go. So the turtle
move forward 100.

Script 6.12

| size caro |
caro := Turtle new.
size := 100.
size := size + size.
caro go: size

Explanation: size is initialized to 100. Then the value of size is reassign to refers to the value of the
expression size + size. As size value was 100, it is reassigned to 200. So the turtle goes forward
200.

3.2 Right or left part of :=


As you may noticed, the symbol size was used with different semantics in the previous scripts. Some-
times, it was used to refer to the value (size + 100.), sometimes it was used to refer to the place holder
itself to change its value (size := 100).

Important!
The key thing to understand variables is to know that the variable name,
always refers to the value associated with the variable, except if the vari-
able is one the left of the constructs :=. In this case the variable name
represents the place holder itself and not the variable value.
We also say that the value of a variable is always read, except when it
appears on the left of := where it is written.

Hence in line 3 of the following script, size is on the left of :=, so it refers to the place holder. In this
place holder we put 1002 . In line 4, size + 100 is on the right side of := (or is not on the left side of
it), so size represents the value associated with the variable, here 100. So the values of size and 100 are
summed up and size := says that the value of right expression (size + 100, here 200) is assigned as
value
2 To of the variable
be exact, a pointer size.
to the objet 100 is stored into the variable
4. Troubleshooting with Variables 57

Script 6.13

| size caro |
caro := Turtle new.
size := 100.
size := size + 100.
caro go: size

Hints. If you have some problems to understand this difference, we suggest you to represent on a piece of
paper a variable has a box with a name. Then when you read in a script the name of the box you substitute
it with the value inside the box, except when the variable appears on the left of :=. In such a case you
modify the value of your box.

4 Troubleshooting with Variables


Using variables offers a lot of expression power, but require a bit of attention. Indeed you can obtain error
by using variables not declared or with wrong values. Note that even the best programmer makes errors.
The only difference is that a good programmer knows how to find his errors. Squeak provides some help.
It checks if a variable is declared or used at all and notify you. Or if you used worng value it opens a
debugger with which you can access the execution state (the receiver of the message, the message, the
variables available...).

4.1 Default value of a variable


When we do not initialize variable, the program have a higher chance to fail because it may happen that we
use variable having wrong or no value. First experiment a bit. Type, select and print the following script.
Script 6.14

| size |
size

Important!
Per default, in Smalltalk the value of a uninitialized variable is nil. nil
is an object that represents undefined values, i.e., values with which we
cannot do anything. Still nil is important because it allows one to know
if a variable has been assigned or not.

The text nil obtained with the last script is a special value assigned to any declared variable. nil
is a special object representing an undefined object. Although it is an object like any other, nil does not
understand many messages. In particular, it does not understand any of the Number or turtle methods.
The following script shows that Squeak analyzes the script and detects before running it that we never
assigned a value to the variable size.
Script 6.15

| size |
size + 10

-2001 Stéphane Ducasse ([email protected])


58 Deeper into Variables

Figure 6.1: Error message when using an undefined variable.

Script 6.16 (Broken script)

| caro size |
caro := Turtle new.
caro go: size.

The error occurring during the execution of the script above generates the message shown in Figure 6.1.
Normally just answer no and initialize the variable.

4.2 A first look at the debugger


We would like to show you how you can find errors. For that any Smalltalk system contains a debugger. In
this section, we just want to give you an overview. For a deeper presentation of the debugger look into the
Chapter ??.
If you answer yes to the question of Figure 6.1 Squeak attempts to send the message "+ 10 " to the
object nil. Of course, this message is not understood by nil. nil understand a very limited set of
messages, so a debugger window opens on the screen (see Figure 6.2). Note that a similar problem can
occurs if you initialized the value with something else than a number. So the process of finding the error is
similar.
If you press the button debug, you obtain the Figure 6.3. The debugger is composed of several panes.
The top pane represents the sequence of all the messages that have been sent just before the error occurs.
This sequence is also called a execution stack. Clicking on of the line in the first pane shows the code of
the message sent in the second pane, as shown in the Figure 6.4. Figurer̃effig:debug1sizenil shows for the
selected message, the code of its definition, here it just shows the script itself. More important the debugger
shows some other information like the value associated with the variable size3 here nil. To see it, just
click on size on the right bottom part of the debugger. We stop now our presentation of the debugger and
will come back for a deep presentation in Chapter ??.

5 Regular polygons with fixed sizes


If you have used Script 6.3 with a large number of sides, you will have noticed that the resulting figure did
not fit on the screen. The next exercise asks you to fix this by reducing the length of the sides when the
number of sides augments.

3 As + was sent to size, self also represents it, you will not what self is in Chapter 7.
5. Regular polygons with fixed sizes 59

Figure 6.2: Debugger window occurring when attempting the execution of a message unknown to an object.

Figure 6.3: Debugger window active just starting to debug.

-2001 Stéphane Ducasse ([email protected])


60 Deeper into Variables

Figure 6.4: Debugger window occurring when attempting the execution of a message unknown to an object.

Experiment 6.1
Modify Script 6.3 so that the size of the regular polygon stays constant as
the number of sides changes.
Hint: introduce a variable length which is set to a fixed length divided by
the number of sides.
7
Teaching Turtles
Up to now, you learned how to write scripts, in which you created first a turtle and sent sequence of
messages to it. Usually you created a turtle at the beginning of each script. Using scripts is a straight
forward approach, but it has its limitations. One of the major limitation is that a script cannot be called by
another script. This is a real problem because you then cannot decompose a problem into smaller and then
simpler problems and then recompose them. You cannot either reuse scripts.
Wouldn’t it be nice if one could use write a kind of script whose sequence of messages could be sent to
any turtle, in any script. Well, this is of course possible and such sequence of messages is called a method1 .
A method consist of a sequence of message having a name; this name can then be used in a script
or even in another method (see Chapter 8). In fact, all turtle instructions used so far are implemented as
methods. As you have seen such methods can be used for any turtle.
In this chapter, you will learn how to define methods. You already know most of the things needed to
write the code of the methods. Methods, however, must be created inside a special editor.

1 Script versus Methods

Let us look at one of the scripts you have been written so far, for example, the script of Exercise 3.4 asked
a turtle to draw a square with a width of 100 pixels. Here it is again:

Script 7.1 (A simple square)

| caro |
caro := Turtle new.
4 timesRepeat: [ caro turnLeft: 90.
caro go: 100]

The problem. Each time you need to draw a square at the current turtle location you need to copy the
2 last lines of Script 7.1. In addition, if you want that the square be drawn by another turtle, marge for
example, you must change everywhere the name caro to marge. This is illustrated by Script 7.2.

1 You will see in Chapter 15 the real semantics associated with methods. Until this chapter, methods are equivalent to functions or

procedure of functional or procedural languages.

-2001 Stéphane Ducasse ([email protected])


62 Teaching Turtles

Script 7.2 (Two simple squares)

| caro marge |
caro := Turtle new.
marge := Turtle new.
marge jump: 200.
marge color: Color red.

4 timesRepeat: [ caro turnLeft: 90.


caro go: 100 ].

4 timesRepeat: [ marge turnLeft: 90.


marge go: 100 ].
Because of all this, working with scripts is not efficient. In fact, we are quite convinced that the two
following statements reflect you personal experience with scripts.
◦ Writing long scripts is a painful task.
◦ Repeating long scripts is boring.
◦ When copying complex scripts, the likelihood of making an programming2 error, such as omitting a
line, is quite high.

What we would like to be able to do! In fact, we would like to define once and only once a sequence of
messages, to give it a name and to be able to send it as message to any turtles just as any predefined turtle
methods such as go:, north, jump:...
If we assume that we define a new method square, and apply this approach to the Script 7.2 we obtain
the Script 7.3 — Do not yet execute it since the method square has not yet been defined.
Script 7.3 (Using the method square)

| caro marge |
caro := Turtle new.
marge := Turtle new.
marge go: 200.
marge color: Color red.

caro square.

marge square

2 How to define a method?


In this section we give a cookbook recipe on how to create a method. For the moment, you can only create
methods for the turtles. In the Chapter 15, we will explain you how to create method to others. Defining a
method requires to choose or create a method categorie (Section 2.2), define the method and then compile
it (Section 2.3).

2.1 The Turtle Browser


Defining methods requires to use a new editor: the turtle browser as shown in Figure 2.1. This browser is
actually a simplified version of the browser used by Smalltalk programmers.
The turtle browser consists of 3 parts:
2 As opposed to a syntax error, which would be caught quickly by the computer. Programming errors, on the contrary, are quite

difficult to catch.
2. How to define a method? 63

Figure 7.1: The turtle bowser showing the code (bottom pane) of the method turn: (right pane) belonging
in the category angle (left pane).

Figure 7.2: The category menu: adding a category.

Categories. The left top part is the category pane. It contains the method categories. Method categories
are just names that help us to group methods together so that we can find information more quickly.
In the Figure 2.1, the category angle grouping all the operations having to deal with turtle direction
changes is selected. Two other categories accessing and color/draw status are shown.

Methods. The right top part is the method pane. It contains the names of the methods of the category
selected in the category panel. In the Figure 2.1, three methods are listed: turn:, turnLeft: and
turnRight:. turn: is the method currently selected.

Code. The bottom part is the code editor pane. It shows the code of the method, whose name is selected
in the method panel. This is also the place where you can enter the code of a new method.

2.2 Creating a new method category


To be able to find easily a method, methods are grouped by categories. A categorie is simply defined by a
name. To start defining a method, you must first define its category if you did not do before.

1. Click with the right mouse button into the category pane. A menu such as the one of Figure 2.2.

2. Select the option add category of that menu.

3. A new window opens, in which you can type the name of the category as shown in Figure 2.2.

-2001 Stéphane Ducasse ([email protected])


64 Teaching Turtles

Figure 7.3: Entering a category name: here ’regular shape’.

Figure 7.4: The resulting category is created.

4. Click into the Accept button to validate your choice.

As shown in Figure 2.2 a result, the name of the new category appears in the category pane and is
automatically selected. The editor is ready to accept the code for the new method. You may choose any
name for the category. Of course, meaningful names are better if you want to share your work with other
people or find your way quickly. You are now ready to define your first method.

2.3 Defining your first method


If the category in which you want to add your method is not selected, select it. Here as you just created the
category, it is currently selected. Then type in the method as shown by Method 7.1 shown below into the
code editor pane.
Method 7.1

square
"Draw a square of 100 pixel size"

4 timesRepeat: [ self go: 100.


self turnLeft: 90]

Typing the method. Typing code into the code editor pane works exactly as with the script editor. Just
delete the text, which is in the code editor pane. Hint: just position the cursor at the beginning of the
editor and click this will select the complete code editor text. Your screen should then look like the one of
Figure 2.3.

Compiling the method. As shown in the Figure 2.3, click with the right mouse button or Option click
into the code editor pane and select the option accept. A new method named square appears in the
2. How to define a method? 65

Figure 7.5: You have typed the method square and now you are asking to compile it.

Figure 7.6: You have typed the method and compile the method square now the browser reflects the fact
that turtles can sent messages with the newly method.

method list pane. The code you typed is reformated by the browser and colored to help you to read the
code (See chapter 1 for preferences if you do not like that).
Note that if you made mistake while typing the method, the compiler will report errors (See Chapter ??
and 6).

Testing the method! You do not have finished yet because the method you type may be wrong so you
should test it. Execute the Script 7.3.

2.4 Stepping back


At this point you should note the universality of a method. Indeed, the method square can be sent to any
turtle as is demonstrated by Script 7.3. This fact has been used since the beginning of this book: commands
such as go:, turnLeft: and so on, are implemented as methods, just like the method square. These
commands have been sent to any turtle.

-2001 Stéphane Ducasse ([email protected])


66 Teaching Turtles

Figure 7.7: A method is composed by a name, a method comment and a method body.

3 What’s in a method?
We have let you type things without much explanation so that you could see the effect before going into
deep explanation. Now is the time to analyze the structure of the method. A method is composed by a
name, an optional method comment and a method body: a sequence of messages as shown by the Figure 3.
To be exact, the method name can also contain arguments (See chapter 9), and the method body can
contain also definition of local variables using | and |.

Method name. The names of methods without arguments such as square follow the same syntax as
variable names. They are composed of alphanumerical characters and start with a lowercase charac-
ter.
In our case the method name is square

Important!
A method name should always represent what the method does and not
how it does it. Imagine that you want to use the method for solving one of
your problem. First you want to know what the method does and not how
it is done.

Method Comment. It consists of a text enclosed within a pair of double quotes ("). Of course, that text
cannot contain any double quote. A comment, however, can be as long as you like and can extend
itself over several lines. In general a comment explains to people what is the purpose and the effect
of the method. In other words, it explains how the method can be used and not how the method is
working. People who want to know how the method is doing its work must look into the method’s
code.
If the method name is explicit enough, the comment may be omitted. In our case, we could have
spared writing a command if we had named the method drawASquareWith100PixelSize. In
fact, defining a method that allows us to specify the size of the square as we will do in Chapter 9,
avoid all this problems.
In our case the method comment is:
3. What’s in a method? 67

"Draw a square of 100 pixel size"

Method body. After the comment, comes the code of the method proper, that is, the sequence of messages
to be executed when that method is sent to a turtle.
In our case the method body is:

4 timesRepeat: [ self go: 100.


self turnLeft: 90]

3.1 Analysing
If you compare the Method 7.1 with the Script 7.1 you will notice following differences:

◦ The line declaring the variable caro is no longer here.


◦ The line creating a new turtle has been suppressed.
◦ In all other lines, caro has been replaced by self.

This should not be a surprise, since we already announced that a method represents a sequence of
messages which can be sent to any turtle: the turtle named caro is not necessarily the turtle to which the
message is sent. Thus, keeping the name caro has little sense. Then, the command is sent to an existing
turtle, that is, a turtle already created. Thus, there is no need to create a turtle since the one we want to send
message to already exists.
If you think a little, you will realize that while defining the method square, we must have a way to
refer to the object that will receive the message square. Indeed, we want to send to the turtle receiving
the message square and only this one, the sequence of messages such as going 100 pixels then turning
90 degree. So we need a way to refer to the receiver of a message: this is the purpose of self. Inside a
method, self represents the object receiving a message.

the self variable. If you remember the discussion of Chapter 5, a variable is just a label for an object
inside Squeak. In particular, we emphasized that the same variable could be used to point to different
objects. In the case of a method, the variable self points to the object that receives the message: when the
message caro square is executed self refers to the turtle named caro, when the message marge
square is executed self refers to the turtle named marge.
To be complete, self is pseudo-variable because it cannot be assigned. Only the interpreter (Squeak)
can assign the value of self. That’s why self does not have to be declared between |. Moreover, self
can only the used inside a method body.

Important!

Inside a method the variable self represents the object that receives the
message. So if messages have to be sent to the receiver, self should be
used to refer to it.

3.2 Deeper into it


Read the following only if all the rest is really no problem for you. As shown by the following method,
note that we could write a method defining a turtle and sending messages to this turtle. However, in such a
case, start should be invoked one another turtle that the one created locally in start and referenced by
the local variable myTurtle. self represents this other turtle: the one receiving the message start.
Do not be worried if you are a bit confused. You will learn more on these issues when we will explain you
how objects work and how to define new ones in Chapter 15.

-2001 Stéphane Ducasse ([email protected])


68 Teaching Turtles

Method 7.2

start

| myTurtle |
myTurtle := Turtle new.
myTurtle go: 100.
self doSomethingElse.

3.3 Method or not

At this stage you may be tempted to go back and convert all scripts you have been doing into methods.
This is not advisable since not all scripts are lending themselves to be turned into a method. In general, one
makes a method when a sequence of messages is sufficiently general to be used many times.

4 Art-nouveau break

As you have seen, it is quite easy to transform a script into a method. As a matter of fact, many seasoned
programmers use scripts to test ideas. When the feasibility of an idea has been proven in the form of a
script, they move the code of the script into a method for later reuse. The next exercise trains you to do
exactly this.
Let us consider the following script, which draws a geometric shape used in art-nouveau patterns.
Script 7.4 (Art-nouveau script)
| caro |
caro := Turtle new.
caro go: 100.
caro turnLeft: 90.
caro go: 100.
caro turnLeft: 90.
caro go: 50.
caro turnLeft: 90.
caro go: 50.
caro turnLeft: 90.
caro go: 100.
caro turnLeft: 90.
caro go: 25.
caro turnLeft: 90.
caro go: 25.
caro turnLeft: 90.
caro go: 50.

Experiment 7.1
Create a method named artNouveau which produces the same figure as Script 7.4.

We now can use this method within a script, say to draw a frame in art-nouveau style.
5. Things to remember 69

Script 7.5 (Art-nouveau frame)

| caro |
caro := Turtle new.
4 timesRepeat: [ caro artNouveau.
caro go: 50 ]

At this point, some astute reader might ask: why don’t we create a method—named artNouveauFrame50,
for example—corresponding to that of Script 7.5. This is indeed possible because any method created for
the turtle object can be reused by another turtle method. This is the topic of the next chapter.

Experiment 7.2
Create a method named artNouveauFrame50 which produces the same figure as Script 7.5 when this
method is used to issue a command to a turtle.

5 Things to remember
1. A method is a sequence of messages which is named. This name can be sent as message to an object.
2. A new method for a turtle is created using the turtle browser.
3. Inside a method the variable self represents the receiver of the message.

Table 7.1: Notion introduced in Chapter 7


Command Description Example
self The variable self represents the self go: 100.
object that receives a message

-2001 Stéphane Ducasse ([email protected])


70 Teaching Turtles
Essential 8
Composing!

In the previous chapter, you learn how to define methods that all the turtles can execute. We show that
defining methods is interesting because (1) methods avoid to have to rewrite scripts and introduce errors
and (2) methods can be used by several and different turtles.
The other main advantage of using methods that we are going to explore in this chapter is the possibility
to compose methods, i.e., to define a method by calling others. Being able to compose methods is extremely
important because we can define a method in terms of another one without having to know all the details
of how the second method is defined. We just call it.

1 Example : the square method

Composing method is quite natural and it is not really new. Indeed, this is what we made in the previous
chapter when we defined a method! For example, the method square is defined by calling the methods
turnLeft: and timesRepeat:. Therefore it is defined in terms of other methods but we do not have
to know how turnLeft: or timesRepeat: are defined.

-2001 Stéphane Ducasse ([email protected])


72 Composing!

Method 8.1

square
"Draw a square of 100 pixel size"

4 timesRepeat: [ self go: 100.


self turnLeft: 90]

2 New Art

In 4, we ask you to define the method artNouveau that draws the picture hereafter. We remind you the
definition of this method if you forgot to save it, so that you can define it now.

Method 8.2
artNouveau
"draws a thing"

self go: 100.


self turnRight: 90.
self go: 100.
self turnRight: 90.
self go: 50.
self turnRight: 90.
self go: 50.
self turnRight: 90.
self go: 100.
self turnRight: 90.
self go: 25.
self turnRight: 90.
self go: 25.
self turnRight: 90.
self go: 50
2. New Art 73

Experiment 8.1
Define the method
artNouveau4 that calls
4 times artNouveau and
produce the figure shown on the
right and that we use as shown
in the script below.
| caro |
caro := Turtle new.
caro completeThing

Experiment 8.2
Define the method artNouveauTurning on which we give the definition below that draws the first
picture of the Chapter.
artNouveauTurning

9 timesRepeat: [self artNouveau.


self turnRight: 10.
self go: 50]

-2001 Stéphane Ducasse ([email protected])


74 Composing!

Experiment 8.3
Define the method
artNouveauGiant that
draws the picture on the right
and whose definition is given
below.
artNouveauGiant

8 timesRepeat: [self artNouveau.


self turnLeft: 45.
self go: 100]

2.1 Stepping Back

Reuse. As you can see it with the methods artNouveau4, artNouveauTurning, and artNouveauGiant,
the method artNouveau is only defined once, but reused several times in different methods. Defining
artNouveau as a method allows one to: (1) define it only once, (2) reuse it in various contexts and (3)
do not introduce errors during rewriting.

Abstraction. If you look at the definition of the method artNouveauGiant, you see that it is de-
fined in terms of artNouveau method that is itself defined in terms of other methods such as go:,
turnLeft:...
In fact, a complex method is often defined in terms of simpler methods and so on because this is
simpler to understand and to define. In Chapter 11 we will show you that to solve a problem it is simpler to
decompose it in terms of smaller subproblem, solve them and compose them. It is essential to understand
that while defining the method artNouveauGiant we do not to know how artNouveau is defined,
we just want to use it!
In fact, when we define a method we are giving a name to a sequence of messages. Doing so we are
reducing the number of details that we have to remember. We just have to remember what does the method
and its name not how it does it. We say that we are building an abstraction.
To really show you this point, we rewrote the method artNouveauGiant with calling the method
artNouveau, we direclty copy the definition of this method (shown in italic) inside the other one.
3. Squares everywhere 75

Method 8.3

artNouveauGiantWithoutCallingArtNouveau

8 timesRepeat: [self go: 100.


self turnRight: 90.
self go: 100.
self turnRight: 90.
self go: 50.
self turnRight: 90.
self go: 50.
self turnRight: 90.
self go: 100.
self turnRight: 90.
self go: 25.
self turnRight: 90.
self go: 25.
self turnRight: 90.
self go: 50thing.
self turnLeft: 45.
self go: 100]

Compare with the method artNouveauGiant and imagine that we do the same with the code of
turnRight:, turnLeft:, and go: because there are also methods! This would be a nightmare of
details.

Important!
We can define a method in terms of other ones and this at various levels
without having to know how these methods themselves are defined.

3 Squares everywhere
Let’s practice. You will now define methods using the method square.

Experiment 8.4
Boxes Define the methods box et separatedBox that produce the pictures shown in figure 3.

Experiment 8.5
Modifying your previous methods to generate various figures.

Experiment 8.6
star. Using the method box, experiment and define a method star that produce the right picture in
figure 3

-2001 Stéphane Ducasse ([email protected])


76 Composing!

Figure 8.1: Stars


Essential 9
Arguments

Up until now, you learnt how to define methods, how to define methods that call other methods. In this
Chapter, you will learn how to define methods whose behavior can be parameterized.

1 Argument, you say?!


The method square defined in the Chapter 7.1 is rather limited because the size of the drawn square is
fixed once for all. You certainly asked yourself what should be done to draw a square with a 300 pixel side
or, 175, or 225 or even 23 pixels sides. There is nothing preventing you to define the methods square300,
square175, square225, square23 and so on. But if we really look at it creating multiple square
methods does not really solve the problem we have here. We would to be able to specify the size of the
square without having to redefine everytimes a new method! We would like to be able to draw squares
whose size is given by the user for example. And for that we do not want to define a method.
What we need is a argument, also named parameter, that is a special variable which can take any value
we like at the moment the command is issued, and not when we construct the method.
Come to think of it, this sounds familiar, doesn’t it? After all, you know that methods such as go: or
turnLeft: do take a value at the time of execution. All we have to do now is to understand how do we
declared a method able to take arguments at execution time, just like the go: command.

1.1 The method square:


We have seen that, in Smalltalk, the name of a method is terminated by a semicolon (:) to indicate that the
method require an argument. Thus, if we want to create a method able to draw a square with an arbitrary
size, we must certainly name it something like square:. The method square: is defined as shown
Method 9.1 and use as shown by the Script 9.1.

-2001 Stéphane Ducasse ([email protected])


78 Arguments

Method 9.1

square: size
"Draw a square of given size"

4 timesRepeat: [ self go: size.


self turnLeft: 90]

Script 9.1 (Using the method square:)

| caro |
caro := Turtle new.
caro square: 10.
caro go: 300.
caro square: 20

When a method having arguments is declared in Smalltalk, the name following the semi colon is a
special variable representing the value that will be given at execution time. This variable is called the
argument of the method. The name of an argument follows the same rules as that of variables. The
argument, however, does not need to be explicitly declared using | and |.

The name of the argument of the method square: is size. This variable is used in the code as the
parameter for the command go:.

1.2 Practicing

Experiment 9.1 (hexagon:)


Let’s start with a simple exercise. Define the method
hexagon: that draws an hexagon of a given side
size.
2. Arguments and variables 79

Experiment 9.2 (Russian squares)


Use the method square: to build squares of differ-
ent sizes as shown on the figure on the right.

Experiment 9.3 (Cross)


Transform the script given hereafter into a method
named cross that draws a cross of a given size.
Hint: notice that 100/ 2 = 50.
|caro|
caro := Turtle new.
4 timesRepeat: [caro go: 50.
caro turnLeft: 90.
caro go: 100.
caro turnRight: 90.
caro go: 100.
caro turnRight: 90.
caro go: 50]

2 Arguments and variables


To understand the similarities and differences between variables and arguments, let’s compare the Script 9.2
and the method Method 9.2 defined earlier that we repeat here.

2.1 Script Analysis


From the Script 9.2 we see that: First the variable size is declared (line 1), then a value is given to it (line
3) and then it is used as parameter of the method go: (line 4).
Script 9.2 (The square script using a variable)

| caro size |
caro := Turtle new.
size := 10.
4 timesRepeat: [ caro go: size.
caro turnLeft: 90]

2.2 Method analysis


First the argument size is declared because it appears after a semicolon in the method name (line 1). Second,
it is used it is used as parameter of the method go: (line 3). An argument does not have to initialized
because it always as a value that is the value specified via the message values.

-2001 Stéphane Ducasse ([email protected])


80 Arguments

Method 9.2

square: size
"Draw a square of given size"

4 timesRepeat: [ self go: size.


self turnLeft: 90]

2.3 Differences
Apart from the fact that an argument is declared within the method’s name, and not using a variable decla-
ration ||, the value of an argument is used in the code of the method like any other variables.
One difference that is specific to Smalltalk is that argument are variables that can only be read i.e., used
as values. Arguments cannot be assigned to new values.
The other major difference is the way variables and arguments are assigned values. The value of a
variable is assigned using the assignment construct := (see Chapter 6). The value of an argument is
assigned when the method to which it belongs to is called. For example, caro square: 10 assigns
the value 10 to the argument size. In the same way than a variable an argument represents a value.
However, an argument represents a value than will be known when the method will be called.

2.4 About calling a method


In fact a method call changes the value of the argument 1
If we take the argument size and self variables of the method square, the message caro square:
100 binds the turtle named caro to the value of self and 100 to the value of size, the message marge
square: 200 binds the turtle named (referenced via the variable) marge to the value of self and
200 to the value of size. As with variable, during the execution of the first message caro square:
100 any use of the argument size will then make reference to the number 100.
To illustrate this difference here is the code for the method polygon100:, which draws a polygon of
size 100 pixels.
Method 9.3

polygon100: numberOfSides
"Draws a polygon of given number of sides, the size of each
side is 100 pixels"

| angle |
angle := 360 / numberOfSides.
numberOfSides timesRepeat: [ self go: 100.
self turnLeft: angle ]

This method has one argument, numberOfSides, and one variable, angle. Both of them are used
within the code of the method. numberOfSides values is specified by a call to the method (for example
7 in the message below), then the variable angle is initialized by computing the angle corresponding to
the argument values (here 360 / 7).

1 This is slightly modified view, because in fact the compiler associates numbers to variables to fetch their values in the collection

of the values passed during a method call.


3. Multiple arguments 81

Script 9.3 (Using the method polygon100:)

| caro |
caro := Turtle new.
caro polygon100: 7.

Remark. You might think that the name of the argument numberOfSides is rather long. However,
this name can easily be understood by any English speaking person reading the method. As we already
discussed in Section 2, it is quite important that anyone be able read your code, almost like a novel. Since
argument names are used like variable names, the discussion of Section 2 is also valid for the names of the
arguments.

3 Multiple arguments
If the name of the method 9.3 gives you a feeling of deja vu, you are right (see Exercise 6.1). Of course, it
would be best to have a method drawing a polygon of given number of sides and size. The problem is how
to create a method having two arguments?
Creating a method with two arguments is obtained by making a name with two semicolons and placing
the argument names after each semicolon. Thus, the method corresponding to the script of Exercise 6.1
can be named polygon:size:. Its code is shown below.
Method 9.4

polygon: numberOfSides size: size


"Draws a polygon of given number of sides and size"

| angle length |
angle := 360 / numberOfSides.
length := 4 * size / numberOfSides.
numberOfSides timesRepeat: [ self go: length.
self turnLeft: angle ]

Implementation remark. The length of the polygon’s side is not equal to the argument size in order
to keep the size of the polygon constant regardless of the number of sides. This requires the use of a second
variable, length, corresponding to the length of the side. This variable is the argument of the command
go:. Our choice was to enforce that the size is equal to the length of the sides when drawing a square.
This is the reason for the multiplication by 4 when computing the length of the side.

Method name. As shown by the Method 9.4 definition, the two arguments of the method are declared to-
gether with the method name. The method name is polygon:size: and the name of the two arguments,
numberOfSides and size, are placed after each semicolon of the method name.

-2001 Stéphane Ducasse ([email protected])


82 Arguments

Important!
Declaring a method with any number of arguments is possible:
there is semicolon for each argument. For example the method
polygon:size: requires two arguments. The definition of the
method polygon: numberOfSides size: size defines two
arguments numberOfSides and size.

4 Other exercises

Experiment 9.4
By slightly modifying the method cross:, define the method crossWidth:height: that allows one
to draw the picture shown in Figure 4. Note that a normal cross correspond to caro crossWidth:
30 height: 60

Figure 9.1: caro crossWidth: 5 height: 50, caro crossWidth: 50 height:


50 and caro crossWidth: 50 height: 5

5 Things to remember
1. A method argument is declared right after the semicolon indicating the position of the argument. It
must not be declared as a variable.
2. The name of an argument follows the same rules as the name of variables.

3. A method with several arguments is declared by adding as many semicolons as there are arguments.
Each argument is declared by placing its name after each semicolon.

5.1 Commands summary


Here is the list of the commands, which you have learned in this chapter.
5. Things to remember 83

Table 9.1: Concepts introduced in Chapter 9


Command Description Example
<methodName>: <argument> Declaration of a method square: size
with one argument
<methodNamePart1>: <argument1> Declaration of a method square: size
<methodNamePart2>: <argument2> with two arguments color: aColor

-2001 Stéphane Ducasse ([email protected])


84 Arguments
Essential 10
Loops and Variables

In this chapter we present how variables and loops can used together. We start by analyzing a simple
but intriguing problem that shows the need of using variables and loops together. We then experiment with
some other problems. We introduce the new messages to:do: and to:do:by: that are more adapted
to situations where sequences of numbers are needed.

1 A Really Strange Stair

Figure 10.1: A strange stair

-2001 Stéphane Ducasse ([email protected])


86 Loops and Variables

1.1 A Strange Affair

Experiment a bit to generate the strange stair shown in Figure 10.1. Start for example by generating a
normal stair and modify it. One of the problem that you should be facing is that the length of each step is
growing regularly.
One of the simplest solution is described by the Script 10.1 where the length of a step grows 10 by 10
pixels. However, such a solution is not satisfactory because we have to compute manually the length of the
next step and that we have to repeat stupidly the same sequence of messages.

Script 10.1 (Strange stair)

| caro |
caro := Turtle new.
caro go: 10.
caro turnLeft: 90.
caro go: 5.
caro turnRight: 90.
caro go: 20.
caro turnLeft: 90.
caro go: 5.
caro turnRight: 90.
caro go: 30.
caro turnLeft: 90.
caro go: 5.
caro turnRight: 90.
caro go: 40.
caro turnLeft: 90.
caro go: 5.
caro turnRight: 90.
...

We would like to be able to use the power of variables and combined it with the one of loops. First
avoiding to repeat the sequence of messages can be possible using the timesRepeat: method. Second
when we analyze the Script 10.1 we see that the length of a step is the length of the preceding plus 10.
Here, 20 = 10 + 10, 30 = 20 + 10, 40 = 30 + 10,.... If we use the variable length to represent the length
of a step, we have: length second step = length first step + 10, length third step = length second step + 10,
length fourth step = length third step + 10. We can express this by the following expression length :=
length + 10.
Let’s combine everything! Now if we take the script of a normal stair (Script 10.2), introduce the
variable length we obtain a similar method and drawing (Script 10.3). Finally, if we modify the value of
the variable length each step of the loop, we obtain the strange stair we want (Script 10.4).

Script 10.2 (A stair with normal steps)

| caro |
caro := Turtle new.
10 timesRepeat: [caro go: 10.
caro turnLeft: 90.
caro go: 5.
caro turnRight: 90]
2. Tools for Understanding 87

Script 10.3 (A stair with normal steps using length)

| caro length|
caro := Turtle new.
length := 10.
10 timesRepeat: [caro go: length.
caro turnLeft: 90.
caro go: 5.
caro turnRight: 90]

Script 10.4 (A strange stair)

| caro length |
caro := Turtle new.
length := 10.
10 timesRepeat: [caro go: length.
caro turnLeft: 90.
caro go: 5.
caro turnRight: 90.
length := length + 10]
In the Script 10.4, the sequence of messages in the loop do: first, make the turtle go forward of a
distance that is given by the value of the variable length, the first time 10, then the second time 20... The
turtle then turns and draws the missing part of the stair. Then the value of the variable length is changed
to take now length current value plus 10.

Experiment 10.1
Change the last line of the loop for example put length := length + 15. Change also the place
where such a line occurs in the loop. Can you explain what happen when you move the last line of the loop
to the beginning of the loop?

Experiment 10.2
Change the Script 10.4 to produce the picture shown on the right.

If you need more time to understand the solution proposed in the Script 10.4, we suggest you to draw
a box representing the variable length, to simulate the messages sent and how the value of length are
changed. If necessary read again the Chapters 5 and 6.

2 Tools for Understanding


Programmers like also to produce traces of their programs. This helps them to understand how the program
behaves and what is going wrong. In Squeak, there are multiple tools for that like the debugger or using
the Transcript. For the moment, we want to show you one simple way of doing this using the Transcript.

-2001 Stéphane Ducasse ([email protected])


88 Loops and Variables

2.1 Transcript
The Transcript, with an uppercase letter, is a global variable whose value is a small window in which
output traces can be written. First, we have to open the Transcript: you have to follow the described
steps: bring the menu and select the menu item ’open’, the select the menu item ’transcript’ (Figure 10.2).
This should open a window, the Transcript that you can resize (Figure 10.3). Within our environment
you can pick the transcript like you made for the script editor or browser in the tools flap on the right.

Figure 10.2: Bring the menu and select ’open’, then select the menu item ’transcript’.

Figure 10.3: Here we have a Transcript

2.2 Using the Transcript


Once the Transcript is open, we can send it messages so that it displays some texts. For example
Transcript show: ’hello’ is a message that asks the Transcript to print the text hello. More
precisely, ” delimits string. ’hello’ is a string composed by the characters $h, $e, $l, $l and $o. In
2. Tools for Understanding 89

Smalltalk a character is represented by the symbol $ and the character letter. To indicate to the Transcript
to print a new line we invoke its method cr. To be able to define useful trace, you need to know how
to concatenate strings together. ’hello’ , ’world’ concatenates the string ’world’ to the string
’hello’. Now if you want to print numbers you have to convert them into strings. 12 printString
returns ’12’ the string that has the character $1 as first element and character $2 as second element.

Figure 10.4: A trace in the Transcript showing the value of the variable length at the beginning and the
end of the loop.

Using these functionalities we can now add trace generation to the Script 10.4 as shown in the Script 10.5.
Executing now the script produces in the Transcript the trace shown in the Figure 10.4.

Script 10.5 (Adding a trace to a script)

| caro length |
caro := Turtle new.
length := 10.
10 timesRepeat: [ Transcript show: ’-start- length value is: ’,
length printString.
Transcript cr.
caro go: length.
caro turnLeft: 90.
caro go: 5.
caro turnRight: 90.
length := length + 10.
Transcript show: ’-end- length value is: ’,
length printString.
Transcript cr]

2.3 Shorthening Message Sequences


In Smalltalk when several messages are sent to the same object, we can create a cascade of messages to
the same receiver (see Chapter ?? for having a complete definition). For example:

| caro |
caro := Turtle new.
caro jump: 100.
caro turnLeft: 90.
caro go: 100

is equivalent to

-2001 Stéphane Ducasse ([email protected])


90 Loops and Variables

| caro |
caro := Turtle new.
caro jump: 100 ; turnLeft: 90 ; go: 100.
The messages go: 100, jump: 100 and turnLeft: 90 are all sent to the turtle named caro. So
the two following code snippets are also equivalent.

Transcript show: ’-start- length value is: ’, length printString.


Transcript cr.

Transcript show: ’-start- length value is: ’, length printString ; cr

3 Practising: Mazes, spirals and other


Now let us see how combining variables and loops helps us to solve some other problems.

Experiment 10.3 (Maze)


Define the method maze that draws the
Figure shown on the right. Hints: the
length of a given maze side is length of the
preceding maze side plus a certain number.

Experiment 10.4 (Spirale)


Define the method spiral: that
given an angle draws a spiral. Have
fun and try different angle values,
you can create wonderful drawing.
The following script created the spi-
ral shown on the right.
| caro |
caro := Turtle new.
caro spiral: 121

Experiment 10.5
Experiment by computing the length of a side, not by adding a distance to the previous side length, but by
multiplying by a given number. Pay attention multiplying by 1.1 means increasing of 10%.
3. Practising: Mazes, spirals and other 91

Experiment 10.6
If we analyze a bit the way a maze or spiral is drawn, we see that it depends on different 4 parameters,
namely the number of side, the starting size of a side, the distance the side is incremented and the angle to
turn. Define a method named spiralSideNumber:startingSize:adding:angle: that draws
any spiral. Try for example the following scripts

| caro |
caro := Turtle new.
caro spiralSideNumber: 50 startingSize: 10 adding: 3 angle: 144.
caro color: Color red.
caro spiralSideNumber: 120 startingSize: 1 adding: 3 angle: 12

Experiment 10.7
Up until now, we created spirals by changing the dis-
tance of which the turtle moved forward and kept
turning with the same angle. Experiment by doing
the opposite, keep the distance fixed but changed the
angle with a fixed increment.
For that define a method with four arguments: the
number of times you loop, the initial value of the an-
gle, the angle increment and the length of a side.
As shown by the figure 10.5, trying to predict the
curves generated by this method is quite difficult. But
fell free to play with different values. Have fun with
it! The figure shown on the right is produced with 390
iterations, an initial angle of 2 degree, an increment
of 20 and a size length of 23 pixels.

Experiment 10.8
If you change the method that you defined in the Experiment 10.7 and
draw a small square instead of drawing a line, You can obtain really crazy
pictures.

-2001 Stéphane Ducasse ([email protected])


92 Loops and Variables

Figure 10.5: Left: iterations: 90, initial angle: 2, increment: 20, length: 30. Right: iterations: 72, initial
angle: 40, increment: of 30, length: 30.

Experiment 10.9
Try to reproduce the Figure shown on the right.

4 Back to reality
In this section we would like to show you two new methods to:do: and to:do:by: that are sometimes
handy when working with loops. Imagine that we want to generate lengths from 14 pixels to 100 pixels 2
by 2. You could write it using timesRepeat: the following way:

length := 14.
43 timesRepeat: [ Transcript show: length printString ; cr.
length := length + 2]

What you see is that 43 = 100 - 14 / 2 and this is not as straightforward as the next solution.

14 to: 100 by: 2 do: [:length | Transcript show: length printString ; cr.]

The loop is executed until the specified upper limit is reach, the limit itself included.

0 to: 12 by: 3 do: [:length| Transcript show: length printString;cr]

produces 0, 3, 6, 9, 12.

0 to: 10 by: 3 do: [:length| Transcript show: length printString;cr]

produces 0, 3, 6, 9.
When you want to increment the value of the variable by 1 you can use to:do: instead of to:do:by:.
5. For the teacher 93

1 to: 5 do: [:length | Transcript show: length printString ; cr.]


1 to: 5 do: by: 1[:length | Transcript show: length printString ; cr.]

both produce 1, 2, 3, 4, 5.
With to:do:by: you can also give negative increment.
10 to: 0 by: -2 [:i | Transcript show: i printString ; cr.]
produces 10, 8, 6, 4, 2, 0.

Some technical details. The last argument of to:do: is a block like for the timesRepeat: method.
However, for to:do: the block requires one argument. Block arguments are similar that method argu-
ment. [:length | ...] is a block defining an argument named length. :length is the argument
name. As blocks may have several arguments, | ends the argument list.

14 to: 100 by: 2 do: [:length | Transcript show: length printString ;


cr.]

Do not give name to a block argument the name of a variable already existing in the script or method
containing the block.

5 For the teacher


Our experience showed that students literally loved to produce maze, spirals and other crazy drawings.
However, in the first version of this book we introduced this chapter just after the variables and realized
that the students where: first losing the notion of finite loops — this was especially true after playing too
much with the last exercises. The problem of such an exercise is that there is no simple stopping condition.
Second, associating variables only with loops. These are the reasons why this chapter arrives so late in the
book

-2001 Stéphane Ducasse ([email protected])


94 Loops and Variables
Essential 11
Decomposing to
Recompose
Most of the time, there is no unique solution to a single problem. Different solutions can be compared
using criteria like the efficiency of the solution, the code readibility or ease of extension. In this book, we
always favor readibility and ease of extension because for most of the problem the safest way is to make
it run, make it right and then only make it fast if needed. Note also that being able to read the code in a
logical way ease its speed up.
In this chapter we would like to show you that a good approach to solve problem consists of decom-
posing a problem into smaller problems easier to solve then compose the smaller solved problems to solve
the main one.
One of the problem with this approach is that there systematic way or algorithm to decompose a prob-
lem, only experience or try and error help. That’s why in this chapter, we propose you some small but not
trivial problems that you should try to solve, then we propose you to decompose the problem into smaller
ones.
As you may expect there are several ways to decompose a problem, in the presented solutions we do
not use recursion, even if certain problems like the golden rectangles could be solved using recursion.
Recursion is the way of writing methods that call themselves until they reach a certain point. Writing
recursive methods lead to writing methods partly in terms of themselves.

1 Golden Rectangles
Let us start with the example the golden rectangles. Here we encourage you not to read the solution we
propose before doing it yourself. For the other problems we do not show a possible solution but propose
you hints.

Problem. We would like to be able to draw several golden rectangles one inside the another one as
shown by the Figure

1. A golden rectangle is a rectangle whose proportion between heigth and width is
1+ 5
equal to 1.6 ( 2 ) and that can generate another golden rectangle having a height equal to the width of
the precedent rectangle. Refer to Experiment 5.4 for the definition of such an interesting rectangle.

Figure 11.1: The first steps of the construction of several golden rectangles.

-2001 Stéphane Ducasse ([email protected])


96 Decomposing to Recompose

Help yourself. Before reading the following, we strongly suggest you to try by yourself to program a set
of golden rectangles. Try to identify possible actions that would help you to achieve your goal. To help
yourself, take a piece of paper and draw a golden rectangle of 10 cm width and one or two golden rectangle
inside as shown by the Figure 1.

A possible decomposition. Drawing several golden rectangles requires to know how to draw one single
golden rectangle, a line to close the square and to compute the size of the next golden rectangle side. We
identify three tasks:

1. We need to draw a golden rectangle that given a width knows how to compute the corresponding
height as asked in Experiment 5.4. Let us name the method implementing this task goldenRectangle:
widthSize where widthSize is an argument representing the width of the golden rectangle.

2. We need to draw the line missing to represent the square included inside the rectangle as shown
by the second rectangle in Figure 1. What we need is to draw a parallel to the width side of the
rectangle. Let us call this method parallelSquareSegment: size that draws a parallel
segment of size size at a distance size regarding the current direction.

3. Once we have the two preceding methods, first we have to draw a golden rectangle, then draw
the missing side of the square and compute the size of the next square. This size is also the next
golden rectangle width. We need also to ensure that the turtle is positioning at the right place,
pointing in the right direction so that it can draw the next square. Let us name the method doing this
goldenRectangles: widthSize atLevel: n where n indicates the number of square
we draw or n + 1 number of golden rectangles.

The goldenRectangle: widthSize method. The following method has nothing special, except
that given the width of the rectangle to be draw it computes its height.

Method 11.1

goldenRectangle: widthSize
"Draw a rectangle with an height 1.6 times longer than widthSize"
"Turtle new goldenRectangle: 100"

| height |
height := widthSize * (1 + 5 sqrt / 2).
2 timesRepeat: [self go: widthSize.
self turnLeft: 90.
self go: height.
self turnLeft: 90]

The method parallelSquareSegment: size The following method is also simple as you can
read it.
1. Golden Rectangles 97

Method 11.2

parallelSquareSegment: size
"Draw a parallel segment of size width at the distance of size
regardings the current direction"
"Turtle new parallelSquareSegment: 100"

self turnLeft: 90.


self jump: size.
self turnRight: 90.
self go: size

The method goldenRectangle: widthSize atLevel: n This method is a bit more com-
plex. Try to understand it yourself before reading the folllowing.
Method 11.3

goldenRectangles: widthSize atLevel: n


"Draw a n + 1 golden rectangles"
"Turtle new goldenRectangle2: 100 atLevel: 5"

| currentRectWidth |
currentRectWidth := widthSize.
self goldenRectangle: currentRectWidth.
n timesRepeat: [ self parallelSquareSegment: currentRectWidth.
self turnLeft: 90.
currentRectWidth := currentRectWidth
* ((1 + 5 sqrt) / 2) - 1)]

Here are some explanations:

◦ As we will need to compute the size of the next square segment, we define line 1 a variable currentRectWidth.
As its name tells it, it represents the width of the current rectangle. Note that it also represents the size
of the square segment that we have to draw.

◦ Line 2, we initialize the value of the variable currentRectWidth to the width of the first golden
rectangle.

◦ Line 3, we draw the first golden rectangle that will serve as base for all the other ones.

◦ Line 4, we define a loops that we repeat the same actions n times.

◦ Line 4, we draw the missing part of the square. The size is the size of the width of the current
golden rectangle.
◦ Line 5, we position the turtle so that the next segment can be drawn.
◦ Line 6, we compute the width of the next rectangle or segment square knowing√that: newW idth =
currentRectHeight−currentRectW idth. Or currentRectHeight = 1+2 5 ∗currentRecW idth.

So newW idth = currentRecW idth ∗ ( 1+2 5 − 1). So using programming concept we obtain:
currentRectWidth := currentRectWidth * ((1 + 5 sqrt) / 2) - 1)

-2001 Stéphane Ducasse ([email protected])


98 Decomposing to Recompose

Experiment 11.1 (Increasing Golden Rectangles)


We implemented the set of decreas-
ing or nested golden rectangles, im-
plement the set of the increasing or
nesting golden rectangles. The right
Figure show some possible steps.

2 Squares, Pyramids and “Damiers”

Now we would like you to try to reproduce the following pictures based on playing with the method
square:.

Experiment 11.2 (Rectangle pyramid)


Using the method square: that draws one square of a given
size and other methods that you would define, draw the pyramid
presented on the right.

Experiment 11.3 (Centered pyramid)


Using the method square: that draws one
square and other methods that you would defi-
neof a given size, draw the pyramid presented
on the right.
3. Centered Squares 99

Experiment 11.4
Using the method square: or other methods
that you would define, draw the picture shown
on the right.

Hints: There are several ways to approach the problems above. You may use nested loops like the
following ones

n timesRepeat: [
n timesRepeat: [ ...]]

But we propose you to define a method lineOf: n squaresWithSize: size that draws a
line of square of a given size. Experiment with this approach. We also suggest you that such a method put
back the turtle in the position where it was before applying the method.

3 Centered Squares

The following exercises should also interest you. Try to do them and define your solutions.

Experiment 11.5 (Mire)


By composing methods, draw the
picture shown on the right.

-2001 Stéphane Ducasse ([email protected])


100 Decomposing to Recompose

Experiment 11.6 (Corridor)


By composing methods, draw the
picture shown on the right.

Hints: One way to solve this problem is to define a method centeredSquare: size that con-
sider the current turtle position as the center of a square of size size and bring back the turtle after drawing
the squre at the same position.
Concept Summary 12
Going in circle

Up to now, you learned how to control turtles, how to write scripts and methods containing loops or
using variables, how to compose methods that call other methods. In this chapter you will learn how to
draw arc or complete circles. What is interesting with this chapter is that you will review all the preceeding
concepts. You may skip some of the sections to only try to ones that seem exciting. Some of them are just
explaining again the same concepts on different examples. You may skip this chapter in a first reading.

1 Arc and Circles


Let us start by learning two new methods: turnLeft:radius and turnRight:radius:. Here is a
script drawing a circle.

Script 12.1 (Simple Circle)


| caro |
caro := Turtle new.
caro turnRight: 360 radius: 100

In this script, we ask a turtle to make a full turn — turnRight: 360 — using a radius of 100 pixels
— radius: 100.
An arc of circle is drawn by specifying an angle lower than 360 degree. You can modify the turn angle,
that is, the 360 in the script above, to a lower number and see what happens.

-2001 Stéphane Ducasse ([email protected])


102 Going in circle

Experiment 12.1
Execute Script 12.1 with a value of the angle lower than 360 degree. Try a value larger than 360 degree.
Does the circle change? Why?

Experiment 12.2
Similarly, you can change the radius, the 100, to another number, to get an
idea of what the radius represents. Execute Script 12.1 with various values
for the radius.

Finally, experiment with turnLeft:radius:. For example, replace turnRight:radius by


turnLeft:radius in all the scripts of the beginning of this chapter.

2 Circles and radius

When a turtle is drawing a circle or an arc of circle, you can think that it is tied to a leash, forcing the turtle
to go along a circle. The length of the leash is the radius of the circle. The point where the leash is fixed
is located on the left or the right of the initial position, in the direction perpendicular to the initial direction
of the turtle. When talking about an arc, the radius is sometimes called the radius of curvature.
Here is a script showing the radius of curvature of an arc. This script is valid for arcs of any angle.

Script 12.2 (Arc with radius)


| caro |
caro := Turtle new.
caro turnLeft: 30 radius: 100.
caro turnLeft: 90.
caro go: 100.
caro south.
caro go: 100

Change the angle of the method turnLeft:radius: in the script above. The radius of curvature is
still shown at each extremities of the arc. Here we have used two things which are worth explaining.

1. Turning left by 90 degree at the end of an arc, points in the direction of the radius of curvature. Thus,
any motion by an amount equal to that of the radius results in showing the radius of curvature of the
arc.

2. The initial direction of the turtle being east, the radius of curvature of the message using turnLeft:radius:
is to the north direction. When the turtle is located at the center of the circle to which the arc belongs,
going south is going along the radius of curvature of the initial position.
3. Do not lose the direction 103

Experiment 12.3 (Juggle Ball)


Reproduce the Figure on the right.

The ancient Chinese believed that all things in nature were build or acting around two contraries. This
kind of philosophy is know to the westerners as the Yin and the Yang. The symbol of this belief can be
drawn using a circle and two arcs of circle as shown below.

Experiment 12.4 (Yin and Yang)


Write a script teaching caro how to draw the symbol of the Yin and the
Yang.

3 Do not lose the direction


At the end of a arc of circle, the direction of the turtle is modified by the same amount as the turn it has
taken. For example here is a path combining a series of arcs and straight lines.

Script 12.3 (Square with rounded corners)

| caro |
caro := Turtle new.
4 timesRepeat: [caro go: 100.
caro turnLeft: 90 radius: 50]

You can now combine the methods turnLeft:radius or turnRight:radius with the methods
turnLeft: or turnRight: to extend the drawing power of the turtle. Here is how to draw a leaf or a
petal:
Script 12.4 (A petal)
| caro |
caro := Turtle new.
caro turnLeft: 45 radius: 100.
caro turnLeft: 135.
caro turnLeft: 45 radius: 100.
The message turnLeft: 135 is used to place the turtle in the proper direction when drawing the
second arcof circle delineating the petal. Try to find the angle needed to draw a fatter petal using arcs of
circle of 60 degree.

-2001 Stéphane Ducasse ([email protected])


104 Going in circle

Experiment 12.5
Rewrite Script 12.4 to draw a petal using arcs of circle of 60 degree.

4 Looping

You can place the messages used to draw a petal into a loop to obtain a flower. Of course, you must ask
caro to turn itself at the end of each iteration.

Experiment 12.6
Try to generate the flower shown on the rigth.

Experiment with the following script to produce some other Figures.

Script 12.5 (Blade)


| caro |
caro := Turtle new.
2 timesRepeat: [ caro turnLeft: 90 radius: 100.
caro turnLeft: 175.
caro turnRight: 90 radius: 100]

Experiment 12.7
Using the Script 12.5 produce the Figure shown on the right.
5. Variation on a tear drop 105

Experiment 12.8
Try to produce the diamond shown in the right Figure.

Experiment 12.9
Define the method blade that draws one blade as shown in the Figure of Script 12.5 and define the method
crossOfBlade that draws the Figure shown in Experiment 12.7.

5 Variation on a tear drop

A portion of the script of Exercise 12.4 used to draw the symbol of the Yin and the Yang can be modified
to draw a tear drop as follows.

Script 12.6 (A tear drop)


| caro |
caro := Turtle new.
caro turnRight: 180 radius: 100.
caro turnRight: 180 radius: 50.
caro turnLeft: 180 radius: 50

You can make the tear drop fatter or leaner by changing the radius of the head or of the tail of the tear
drop. This is the topic of the next two exercises.

Experiment 12.10
Modify Script 12.6 to draw a fatter tear drop.
Hint: The radius of the head must be larger than that of the tail.

-2001 Stéphane Ducasse ([email protected])


106 Going in circle

Experiment 12.11
Modify Script 12.6 to draw a leaner tear drop.

As you have seen, each time you modified the radius of the head of the tear drop you must also modify
the radius of its tail. To do Experiment 12.10 and Experiment 12.11 you could have written a script by
changing one number, namely how much of the radius must be changed. This can be done by using
variables to hold the value of the radius of the tail and of the head. Here is the resulting script.

Script 12.7 (A parametrized tear drop)


| caro headRadius tailRadius|
caro := Turtle new.
headRadius := 65.
tailRadius := 100 - headRadius.
caro turnRight: 180 radius: 100.
caro turnRight: 180 radius: headRadius.
caro turnLeft: 180 radius: tailRadius

In Script 12.7 the declaration line contains two variables in addition to the name of the turtle: headRadius
and tailRadius. The line headRadius := 65 after the line creating the turtle must be read as the
line before it. This line tells Squeak that the variable headRadius is now used to hold a new object,
namely the number 65. From now on, we can use this variable in all places where we would have used the
number 65: the computer keeps track of what is in the name.

In particular, we can use the variable headRadius if any calculation such as the ones we have seen
in Section 4.2. Since the tear drop must be constructed such that the radius of the tail must be equal to 100
minus the radius of the head, we can instruct Squeak to perform this operation for us and ask it to hold the
result of the operation in a new variable. This is exactly what the next line—tailRadius := 100 -
headRadius—is telling Squeak: the variable tailRadius contains a number equal to 100 minus the
content of the variable headRadius.

Finally, the two variables headRadius and tailRadius are used in the turtle messages to draw the
tear drop. You can now play with Script 12.7 to modify the shape of the tear drop by changing one radius
only.

One can go a step further if you want also to modify the size of the tear drop. In this case you must
change the radius of the first arc message: let us call this variable size. Here is the new script.
6. De composition and Composition 107

Script 12.8 (A tear drop of any size)

| caro size headRadius tailRadius |


caro := Turtle new.
size := 100.
headRadius := 65.
tailRadius := size - headRadius.
caro turnRight: 180 radius: size.
caro turnRight: 180 radius: headRadius.
caro turnLeft: 180 radius: tailRadius.

Since none of the radius of the tail or the head of the tear drop can exceed the size of the tear drop, it
would be best to introduce a new variable, ratio, whose value must be comprised between 0 and 1 to
describe the relative size of the radii of the head and the tail of the tear drop. Here is the final script.

Script 12.9 (A tear drop of any size)

| caro size ratio headRadius tailRadius|


caro := Turtle new.
size := 100.
ratio := 0.65.
headRadius := ratio * size.
tailRadius := size - headRadius.
caro turnRight: 180 radius: size.
caro turnRight: 180 radius: headRadius.
caro turnLeft: 180 radius: tailRadius

By changing the values assigned to the variables size and ratio you can draw many tear drops of
various sizes and shapes.

6 De composition and Composition

We would like to be able to drw a palm tree as shown by the following Figure 12.1. Use a variable to
represent the lenght of the palms. Experiment a bit before reading the following...

The problem is how to get the turtle back to the place where it was before starting to draw a palm. The
following method does exactly that. Defined it and the symetric one to turn on the right, then draw this
palm tree. Note that playing with it you could also generate the Figure 12.2.

-2001 Stéphane Ducasse ([email protected])


108 Going in circle

Figure 12.1: A palm tree

Method 12.1

goAndBackTurnLeft: anAngle radius: aRadius


self turnLeft: anAngle radius: aRadius.
self turnLeft: 180.
self turnRight: anAngle radius: aRadius.
self turnLeft: 180
7. Loops and Variables 109

Figure 12.2: Nearly a nice logo for a coconut company

7 Loops and Variables

Experiment 12.12
Draw the shown spiral using the
method turnLeft:radius:.

-2001 Stéphane Ducasse ([email protected])


110 Going in circle

8 Things to remember
1. A circle of radius 100 pixels is drawn with the message turnRight: 360 radius: 100 or
turnLeft: 360 radius: 100.

2. An arc of circle is generated with the same message by specifying an angle lower than 360 degree.
3. The direction of the turtle after drawing an arc of circle is changed by the same amount than the
angle of the arc of circle.

8.1 Methods summary


Here is the list of the methods, which you have learned in this chapter.

Table 12.1: Methods introduced in Chapter 12


Method Description Example
turnLeft:radius Asks a turtle to draw an arc of cir- caro turnLeft: 90 radius: 100
cle of given angle and given radius
oriented to the left
turnRight:radius Asks a turtle to draw an arc of cir- caro turnRight: 90 radius: 100
cle of given angle and given radius
oriented to the right
Concept Summary 13
When to stop a loop

In the previous chapter, we have seen how to use variables to construct general scripts. For example, the
number of repetitions in the script of Exercise ?? can be increased to a large number. Above 100, however,
you will not be able to see any difference on the screen because the iterations above the 100th (or so1 ) are
no longer visible on the screen. The question we shall try to answer in this chapter is when to stop a series
of repetitive commands.

1 This number depends of the resolution of your screen.

-2001 Stéphane Ducasse ([email protected])


112 When to stop a loop

1 A quick answer
The answer to the introductory question can be very easy in some cases. For example, when drawing a
square using Script ??, the number of repetitions is 4. Executing the loop more than 4 times has little sense:
caro will simply draw again the lines it has already drawn. Quite useless indeed!
When using problems involving variables, however, the answer to the question can be quite tricky. Here
is an example of a script where we want to draw a spiral. Such a spiral is constructed with arcs of circle of
increasing radius.

Script 13.1 (A spiral)


| caro radius step |
caro := Turtle new.
step := 10.
radius := step.
25 timesRepeat: [ caro turnLeft: 90 radius: radius.
radius := radius + step].

The question is: how often should I repeat the loop so that all the arcs are visible? No, the answer to
the question should be valid when the value of the step is changed. In other word, the response will vary
depending on the values used in the script. A way to answer the question would be: one will stop whenever
the radius of the arc exceeds a given value. That value depends of course on the size of your screen.
The command whileTrue: gives you the possibility to instruct Squeak to stop a repetitive series of
command. Let us first see how it looks, then we shall discuss it in more details.

Script 13.2 (A finite spiral)

| caro radius step |


caro := Turtle new.
step := 5.
radius := step.
[ radius < 360 ] whileTrue: [ caro turnLeft: 90 radius: radius.
radius := radius + step ].

If you change the value of the variable step in Script 13.2, the number of turns of the resulting spiral
will change so that the last arc of circle fits on the screen.

Remarque. The half of the height of our screen was about 360 pixels. The size of your screen may be
different: if that is the case, change the value 360 in Script 13.2.

2 True and false


Let us now analyze how the new command whileTrue: is working.
Like for the command timesRepeat:, the argument of the command whileTrue: is a block of
code.
The object to which the command whileTrue: is sent, however, is a block of code instead of being
an integer. This is the reason for the brackets around the expression radius < 360. We shall call that
3. Beware of looping forever 113

block the conditional block to differentiate it from the argument. Inside these brackets, one can in principle
have any series of commands: the result of the last of these commands must be a Boolean expression, that
is, an expression which can take the values true or false.

Remarque. The adjective Boolean comes from George Boole, an English mathematician of the nine-
teenth century. He discovered that logical propositions could be manipulated like mathematical objects.
In our case, the last and only expression of the conditional block is radius < 360 which reads: is
the value of the variable radius lower than 360? If the result of evaluating the conditional block is true,
the series of commands contained in the block of code used as argument to the command is executed; after
the execution, the conditional block is evaluated again and the same decision is taken. Thus, the iteration
proceeds as long as the evaluation of the conditional block yields the value true.

3 Beware of looping forever


With the command whileTrue: you are entering into the major league of programming. It is a very
powerful command, often used in programming as you will discover in the rest of this book.
Like many powerful tools, however, it is also dangerous. Of course, not dangerous as in you will cut
your fingers with it. Nevertheless, you can cause endless work to a turtle with such command. Consider—
but, god forbids!, do not execute—the following script:

Remarque. Script 13.3 is not meant to be executed. Attempt to execute it will block your computer.

Script 13.3

| caro radius step |


caro := Turtle new.
step := 5.
radius := step.
[ step < 360 ] whileTrue: [caro turnLeft: 90 radius: radius.
radius := radius + step.].
The conditional block in Script 13.3 is always true since the variable step is not changed by the
expressions inside the loop. Thus, the series of commands within the argument block will be executed
forever until someone pulls off the plug of your computer.2
The morale of this section is that, whenever you are using a command, such as the command whileTrue:,
you must absolutely make sure that the condition for the execution of the iteration will indeed cease to be
true after a finite amount of time.

4 Predicting
The command whileTrue: can be used efficiently to simulate the behavior of some animal. In this
section, we shall illustrate how this command cen be use effectively to simulate an animal with whiskers.
If you have a pet such as a cat or a rabbit, you have noticed that touching the tip of the animal’s
whisker makes him react. Whiskers have been proven a good way for an animal to explore its immediate
surroundings without seeing. For our simulation, we are going to equip our computer turtle with a whisker
allowing it to probe ahead of it.
We are going to write a script moving a turtle until it meets a wall. The wall will be represented by the
trace left by a first turtle named marge. Here is the script creating the wall located at 275 pixels east of the
point at which all turtles are created. The wall is painted blue.
2 Well not quite, nasty things will occur before that, such as memory and/or arithmetic exception.

-2001 Stéphane Ducasse ([email protected])


114 When to stop a loop

Script 13.4

| marge wallDistance |
wallDistance := 275.
marge := Turtle new.
marge color: Color blue.
marge jump: wallDistance.
marge south.
marge jump: 150.
marge north.
marge go: 300.
We now shall add to this script the motion of caro. Here we need to introduce a notion which we
shall develop in a later section: how to identify the position of a turtle? To simplify, we have created the
command horizontalLocation, which returns the location of the turtle along the eastern direction.
With the help of the command horizontalLocation caro can decide to move by small steps at
a time and, at each step probe whether the distance between the wall and the turtle is larger than the size of
the whisker. As long as this is the case, the turtle can move by a whisker’s length. Here is the full script.

Script 13.5

| marge wallDistance wallPosition caro whiskerSize |


wallDistance := 275.
marge := Turtle new.
wallPosition := marge horizontalLocation + wallDistance.
marge color: Color blue.
marge jump: wallDistance.
marge south.
marge jump: 150.
marge north.
marge go: 300.
whiskerSize := 5.
caro := Turtle new.
[ caro horizontalLocation + whiskerSize < wallPosition]
whileTrue: [ caro go: whiskerSize].
The first part of the script simply uses the turtle called marge to draw the wall at a given position as shown
before. The difference is that the position of the wall is computed in the variable wallPosition, taking
into account the location at which all turtles are created. Then, the turtle called caro is put into motion.
The variable whiskerSize is the size of the whisker. You may change the size of the whisker to see the
effect on the motion of caro: the longer the whisker, the farther from the wall will caro stop.

5 Things to remember
1. The command whileTrue: can be used to repeat a series of command when the number or repe-
tition is not known in advance.

2. The object to which the command whileTrue: is sent is a block of code. The command is
repeated as long as the value of the block of code is true.

3. One must be sure that the condition of the command whileTrue: will be fulfilled after a finite
number of iterations.
5. Things to remember 115

5.1 Commands summary


Here is the list of the commands, which you have learned in this chapter.

Table 13.1: Commands introduced in Chapter 13


Command Description Example
whileTrue: repeat a series of commands while
a given condition is true

-2001 Stéphane Ducasse ([email protected])


116 When to stop a loop
Concept Summary 14
Finding Information
Give a man a fish and he won’t starve for a day. Teach a man how to fish and he won’t starve
for his entire life. (check the source)

Squeak is a composed by a small language as we started to present but possesses a really big library of
fonctionality from multimedia to encryption passing by graphics and network. Squeak is so rich that even
by restricting ourselves to a extremely small subset we could explain it to you in details. However, what we
can do is to explain you how to find information you need. In this chapter, you will learn how to discover
the behavior of certain objects such as numbers, points and rectangles by yourself. In Chapter ?? we will
come over on this topic and present the aspects and other tools in the context of object programming.

1 Method Finder
Squeak possesses a unique and powerful tool called the MethodFinder. This tool is available in the default
environment of Squeak in the flap tools but also in the flap caro-tools of our environment as shown by the
figure ??. To use MethodFinder, drag it from the flap into the desktop, you should obtain a tool as shown
in the figure 1.

Looking for a method based on its name. Imagine we would like to know if there exists in the system
a method that computes and returns the quotient of two numbers and that we suspect that such a method
name contains the quo string. We just type quo in the top left pane of MethodFinder and type return. The
second pane gets filled with all the methods that contain quo in their names. Scroll a bit the list and you
will see that quo: exists. Now when you select quo: in the list, the top right pane gets filled with the list
of classes that effectively implements such method. Here we have the classes Integer, LargePositiveInteger,
Number and SmallInteger. If you click on the class list, a default squeak browser called a system browser
will be open on the method so that you can its code. As we do not need to know the code of a method to
use it, don’t do it right now. This situation is shown in the figure 1. This means that now you can type 11
quo: 2 and get 5.

Figure 14.1: The MethodFinder thumbnail in the caro-tools flap. Drag it from there to the desktop to obtain
one MethodFinder tool.

-2001 Stéphane Ducasse ([email protected])


118 Finding Information

Figure 14.2: A MethodFinder showing all the methods containing the string quo and the classes in Squeak
defining the method named quo:

Script 14.1

11 quo: 2
-> 5

Looking for a method based on its behavior. This approach works when you have an idea of the possi-
ble name of the method you are looking for. But the MethodFinder is much more powerful. What we can
do is to give it some values and the result we expect and it will try to find the methods satisfying them.
Let us try to find the method that computes the integer quotient of two numbers. This method should
behaves like shown in the following script:

Script 14.2

11 xxxx 2
-> 5

10 xxxx 2
-> 5
In the top left pane, type the expression 11 . 2 . 5 and type return. 11 . 2 . 5 means
that we expect to get 5 if we send a message to the number 11 with the number 2 as argument. The dot
and space are used to separate the arguments and result. You get in the second pane the list of methods that
when sent to 11 with 2 as argument returns 5. As shown in figure 1 you get two methods // and quo:.
Again when you select one method name, you will see the class implementing in the top right pane.
@@NEED ANOTHER EXAMPLE@@

2 Providing multiple argument-results couples.


You can also propose a set of arguments-result couple. Imagine that we are looking for a method that
implements the logical or. This means that combines boolean values the following way: true and true
should return true while true and false should return false.
If you type these two expressions true . true . true and true . true . false one after the other one you
obtain two lists of methods and you have to identify the common methods to know which one satisfy both
constraint sets. It is possible to get the result directly by evaluating and printing the following expression
in which we recognize our both expressions.
2. Providing multiple argument-results couples. 119

Figure 14.3: A MethodFinder showing all the methods that returns 5 when sent to 11 with 2 as argument.

Script 14.3

MethodFinder
methodFor: { {true . true} . true . {true . false} . true}
Printing such an evaluation returns a list of methods containing in particular | that represents the logical
or and which indeed satisfies the following constraints true | true returns true (true or true is true)
and true | false returns true (true or false is true).
For advanced use you should understand that the MethodFinder does not take into account all kinds of
objects. It works on the simple objects such as characters ($a), strings (’hello’), numbers, booleans
(true and false).

-2001 Stéphane Ducasse ([email protected])


120 Finding Information
Part II

Objects

-2001 Stéphane Ducasse ([email protected])


Concept Summary 15
Objects, Messages, and
Classes
Even if we used the terms objects in the previous chapters, we only presented programming concepts like
variables, loops, abstraction, arguments that are not specific to object-oriented programming. This was
possible because object-oriented languages share common concepts with other programming languages
such as procedural programming. In this chapter, we present the notion of objects and classes which are
central to object-oriented programming. Objects are entities that react to messages by performing certain
tasks and that hide the way they specify these tasks. Objects are created by classes that represent the
behavior of family of objects.

1 Objects: Entities Reacting to Messages


Up until now you have been programming in Squeak which is an object-oriented programming language
but without really using objects. You mainly created objects and sent them messages without really paying
attention because this was natural. As you saw, you created objects, turtles but also numbers, colors and sent
them messages (Figure 15.1 recaps the relationship between messages, method selector, message receiver
and message arguments.). In reaction the objects carried some computation and performed some actions.
For example, a turtle knows how to change its direction, go forward...

An object is a computer entity that once created receives messages and performs some
actions in reaction.

In fact, an object executes a method in response to a message reception. A method is a named sequence
of expressions and messages that represents the behavior of the method as shown in Chapter ??. It can
also send messages to other objects. For example, when a turtle draws a line it send messages to an object
representing the line color.

1.1 An Object has a Unique Identity


An object has an identity. Each object is unique and can be uniquely identified. In Smalltalk, == allows
one to test object identity as shown by the following examples.
Script 15.1 (Testing object identity (1))

| caro mag |
caro := Turtle new.
marg := Turtle new.
caro == mag
returns false
Two different turtles are created and referred to by the variables caro and marg. As they are different
the test returns false.
-2001 Stéphane Ducasse ([email protected])
124 Objects, Messages, and Classes

Figure 15.1: Messages, method selector and expression. Note that the message receiver and the message
arguments can be expression too as shown by (b) and (c).

Figure 15.2: Composing expression by other expressions (in grey in the figure). For example, caro
jump: 100 + 10 is composed by the expression 100+10.
1. Objects: Entities Reacting to Messages 125

Script 15.2 (Testing object identity (2))

| caro mag |
caro := Turtle new.
caro == caro
returns true
Only one turtle is created and assigned to the variable caro.

Script 15.3 (Testing object identity (3))

| caro mag |
caro := Turtle new.
mag := caro.
caro == mag
returns true
"because the two variables refer to the same object"

Here we create only one object but refer to it by two different variables. However this is not changing
anything because the two variables refer to the same object that has the same identity soo we get true
meaning that the identity of the object pointed by caro is the same than the one pointed by marge.

An object has an identity.

1.2 An Object is a Secret Entity


An object is responsible of the way it realizes its reaction to a message. It offers services but hides the
way they are implemented. What is important to realize is that we do not have to know how the method
associated with the message selector is implemented. Only the object knows the exact definition of the
method. When you defined the method square: that specifies how a turtle draws a square of a given
size, you were focusing on how a turtle draws a square, i.e., the sequence of messages that leads to a square.

An object is responsible of the way it realizes its reaction to a message. It offers


services and hides the way they are implemented.

Method 15.1

square: anInteger
"make the receiver draw a square of size anInteger"

4 timesRepeat: [self go: anInteger.


self turnLeft: 90]

From a turtle user point of view, the only relevant information is that the turtle effectively receiving
the message square: will execute the method that draws a square. So changing the definition of the
square: method to the one of method 15.2 does not have any consequence on the methods that call it 1 .

1 Note that such a behavior is not specific to object-oriented programming but we think that it is worth to show again that abstraction

is important as presented in Chapter ??.

-2001 Stéphane Ducasse ([email protected])


126 Objects, Messages, and Classes

Figure 15.3: A turtle is an object which has an interface, i.e., a set of messages to which it can reply and a
private state that only its methods can acess.

Method 15.2

square: anInteger
"make the receiver draw a square of size anInteger"

self go: anInteger.


self turnLeft: 90.
self go: anInteger.
self turnLeft: 90.
self go: anInteger.
self turnLeft: 90.
self go: anInteger.
self turnLeft: 90

1.3 An Object protects its Data.


An object holds some private data that represents its state. Moreover, it controls its state and should not
let other objects play directly with them because this could let him into an unconsistent state. For example,
you do not want to somebody else plays with the data of your bank account directly and really want to
control your transaction. For example, a turtle can be represented by a position, a direction and a way to
indicate if its pen is up or down. But we are not able to direcly access these data and change them. For
that we have to use the set of methods proposed by the turtle. This set of methods can be called services
or interface of an object. We say that the object state is encapsulated, this means that not everybody can
access it. In fact, object-oriented programming is based on encapsulation, i.e., the fact that per default
objects are the only ones that can access their state.
In Smalltalk, there is no way to access the state of an object if the object does not define a method to
access it. An object controls its state and should not let other objects play directly with them because this
could let him into an unconsistent state. Moreover clients should not rely on the internal representation of
an object because an object is free to change the way it is implementing its behavior.
A turtle does not provide a way to change its direction directly, nor to change directly the status of its
pen. jump: and go: are actually changing the status of the turtle’s pen and its position.

Teacher’s Corner
Traditionally a logo turtle provided explicit commands to lower or upper its pen. This leads to
programs where the state of the pen have to have been first set then removed as shown by the
following script:
2. Classes: Object Factory 127

Figure 15.4: Two turtles have the same interface, i.e., set of messages being understood but they have
different private state representing their direction, position and pen color.

Script 15.4 (An example of state changes)

| caro |
caro := Turtle new.
caro penDown.
4 timesRepeat: [caro go: 100. caro turn:90].
caro penUp
The turtle we propose do not have such a possibility but offers the go: method that move
forward its receiver and make a trace and the method jump: that only move forward. We
made this choice to really stress the fact that objects are about behavior and that state in one
way to represent the behavior of an object.
Teacher’s Corner
Designer Hints
Exposing the internal state of an object by defining methods providing access to it weakens
the control that an object has over its own state (see chapter ??).
Designer Hints
An object holds some private data that represents its state.

2 Classes: Object Factory


As we already saw several turtles can be created independently of each others. In the following script both
caro and marg execute the method named jump:. As a result they change their respective position.
Script 15.5 (Two turtles understanding the same messages)

| caro marg |
caro := Turtle new.
marg := Turtle new.
marg jump: 100.
caro jump: 100.
marg jump: 200.
The method jump: executed by caro is the same as the one executed by marg. To avoid the dupli-
cation of the behavior in all similar objects, class-based object-oriented languages like Smalltalk introduce
the notion of class. Here all the behavior understood by caro and marg is defined in the class Turtle.
This behavior is shared by all the turtles. Hence, every time we define a new method in the class Turtle,
all the turtles can execute it.
Example: Once the method square: is defined caro, marg or any other turtles can execute it.

-2001 Stéphane Ducasse ([email protected])


128 Objects, Messages, and Classes

Figure 15.5: The class Turtle using the UML notation is described by a name, a list of instance variables
and the methods it implements. The instance variables describe the state of the two instances. The instances
of a class have different internal states but share the same interface. They can answer the same set of
messages.

Factory of Objects. A class is a factory of objects, called its instances. marg and caro are instances
of the class Turtle. 1 is an instance of SmallInteger. In Smalltalk, any object is instance of a class.
A class creates and returns an instance in response to the message new. As class defines the behavior of
objects, all the instances of a class share the behavior defined by the class.

A class is a factory of objects, called its instances. A class defines the behavior of all
its instances.

Script 15.6 (Any object knows its class)

| caro |
caro := Turtle new.
caro class printString
prints Turtle
Any object knows its class. In fact such an information is crucial because when a message is sent to
an object, the corresponding method to be executed is not defined in the object but in its class. This way
we avoid duplication of methods and spare space. The way methods are found is presented in detail in
chapter ??.

2.1 Instance State Description


In addition to be a recipient for object behavior, classes acts as factory of similar objects. Indeed, caro
and marg are both instances of the class Turtle and they both have somehow a position, a direction and
a color. This common internal representation or instance state is described by the class Turtle.
A class specifies the instance internal state that represents by means of instance variables. Instance
variables are the variables that each instance of the class has and whose values represent the state of the
instance.
In the following definition2 of the class Turtle we see three instance variables: direction, position
and color. These three instance variables fully describes a turtle.
2 Note that the definition in the environment is different and we will come to that in Chapter ??.
2. Classes: Object Factory 129

Figure 15.6: When a message is sent to an objects, the corresponding method is looked up in its class. The
fine dashed line represents the lookup of the method that goes from the receiver to its class.

Class 15.1

Object subclass: #Turtle


instanceVariableNames: ’direction position color ’
category: ’Caro-Turtle’

In the sentence “A class describes the internal structure of all its instances” we would like to stress
the fact that the class does not define the state of its instances, just describes it. Hence any instance can
have different values associated with the same instance variables. caro and marg both have a direction
instance variable as specified by the class Turtle but they both can have different value associated with
it. Hence caro’s direction may be 90 while marg’s direction may be 0 as shown in figure 15.5.

Instance variables are then special variables that can be accessed when defining a method. Instance
variables follow the same rules than the ones we explained in the Chapter 5. One of the differences is
that they do not need to declared. In fact they are declared in the class definition. Contrary to variable
used in script, named temporary variables, which do not exist after the script execution, instance variables
last as long as the instance does. As any other variables they have to be initialized. This instance variable
initialization occurs just after creating a new instance, we will see that in the following chapters. Chapter ??
presents in details such aspects.

A class describes the internal structure of all its instances.

For example, the method turn: method 15.3 requires to increment the receiver’s direction by the
value of method argument. This is what the method turn: does, it accesses the current value of the
instance variable direction and change its value by adding it the method argument.

-2001 Stéphane Ducasse ([email protected])


130 Objects, Messages, and Classes

Method 15.3

Turtle>>turn: degrees
"Change the direction so that the receiver turns by an amount
equal to the argument, degrees. degrees represents the rotation in
degrees.
The positive sense is nonclockwise. The negative sense is
clockwise."

direction := direction + anInteger.


self changed

The method method 15.3 illustrates that instance variables are accessible from all the methods of the
class they belongs to.

Instance variables are accessible to all the methods of a class. Instance variables last
the same life-time than the object to which they belong to.

In Squeak, instance variables cannot be accessed from outside of an object. Instance


variables are only accessible from the methods of the class that define them.

2.2 Summary
It is important to see

◦ that all the instances understand the same messages defined in their class, one single instance cannot
have a specific method. caro cannot understand a method that marg could not.
◦ that all the instances of a class are represented by the same internal structure describe in terms of
instance variables,
◦ but instances have different and independent states, i.e., the values of the instance variables that rep-
resent them change from instance of instance. caro can be at a different position than marg.

Let us explore the analogy between a class creating instances and real machine producing objects like
cans. A machine is designed to produce cans having certain properties like size, containment, resistance...
Somewhere a machine possesses a kind of instance variables representing the properties that any cans it
produces will have. So all the cans produced by a given machine with the same constituents will have the
same property and react similarly to the same events. However, a specific can be crushed and its property
like containment can change.

Objects are unique entities that reacts to message. A class describes the state (instance
variables) and the behavior (methods) of all its instances. The state of an instance is
the value of its instance variable and is instance specific while the behavior is shared
by all the instances.

Methods define the behavior of all the instances of the class they belong to.

Class and instances are different. Class and objects are different objects, they understand different
messages. For example, sending new to the Turtle class returns a newly created turtle, while sending
new to a turtle results in an error. In the opposite way, sending jump: to Turtle leads also to an error
because Turtle is a factory of objects not the objects themselves.
3. What you should have learned 131

2.3 Objects Reacting Differently to the Same Messages


@@ Should may be moved with the chapter on AniTurtle@@
Different objects can react differently to the same messages. For example, an animated turtle under-
stands the same messages than a basic turtle. However, it reacts differently. An animated turtle performs
extra actions related to its animation. Having different behaviors associated with the same message and
depending of the message receiver is called polymorphism. Object-oriented languages are based on poly-
morphism.
Script 15.7 (An Animated Turtle)

| caro |
caro := AnimatedTurtle new.
caro go: 100.
caro turnLeft: 90

Different objects can react differently to the same messages.

Script 15.8 (A Turtle performing the same actions)

| caro |
caro := Turtle new.
caro go: 100.
caro turnLeft: 90
Note that in the context of the Turtle and AnimatedTurtle objects the method execution results
in similar, yet different, actions. The receiver arrives at the same location in the same state but performs
extra action, the animation for the animated turtle. It may happen that different objects understand the same
message while performing completely different actions but this is rare.
Most of the time it is better to give similar name to methods performing similar effect, and different
names when the methods are performing semantically different actions, so that users of the objects are not
lost.
As two different classes can define different method with a similar name, it may be confusing for the
reader to clearly identify the class to which a method belongs to. Such an issue does not occur in the
environment because a method is always shown in the context of its class (see Chapter ??). To solve this
problem, we use the following convention: we prefix the method selector with the name of the class and ».
Hence the method square: defined in the class Turtle is written as follow:
Method 15.4

Turtle>>square: anInteger
"make the receiver draw a square of size anInteger"

4 timesRepeat: [self go: anInteger.


self turnLeft: 90]

3 What you should have learned


An object is an entity with a unique identity that reacts to message sends by executing methods (sequences
of expressions). Objects are instances of classes, i.e., objects are created by classes that are object factories.
Object state and behavior are described by their class. Object state is described in terms of variables named
instance variables. Object state is specific to each instance while all instances of a class share the same
behavior. An object does not let other objects plays with its private data and control the interface it offers
to other objects.

-2001 Stéphane Ducasse ([email protected])


132 Objects, Messages, and Classes
Concept Summary 16
Using an Inspector
Missing( dessin qui montre que les deux cotes de l’inspecteur correspondent en fait a differents niveaux
classes, instance)
Squeak allows one to interact in a close manner with objects. In Squeak objects are living entities
with which we communicate by sending messages. Moreover this object liveness is emphasized by (1) the
incremental compilation, i.e., the fact that classes, methods and objects can be created at any time, (2) by
the possibility to send messages to objects as soon as the methods have been defined and (3) by a set of
powerful tools such as the code browsers, the debugger and the inspector. The inspector is an object that
allows you to tinker other objects when you are debugging your code. In this chapter, we present how we
can inspect objects and use an inspector to communicate with objects.

1 Opening an Inspector
To get an inspector on any object, send it the message inspect. Alternatively you can select an expres-
sion and bring the menu inpect it or use the key shortcut i. For example the script 16.1 creates a new
FlasherMorph (a small blinking morph), opens it in the morphic world and inspect it. The inspector
shows the instance variable values such as the color of the border or the color of the flasher when it is on.
Look the bounds instance variables and move the flasher by grabbing it directly and see the values of the
bounds changing accordingly.

Script 16.1 (Inspecting a flasherMorph)


| flash |
flash := FlasherMorph newStandAlone.
flash openInWorld.
flash inspect

With morphs, the graphical objects, we can use the halos (command click) as shown in figure 16.1 and
select under the item debug... the choice inspect morph as shown in the figure 16.2.

2 Class and Instances


The figure 16.3 shows that an inspector is in fact represents two different kind of information. On the left
side is shown the names of all instance variables that the class of the object defines1 . In addition the first
element self represents the inspected object itself. On the right side, the values of the currently selected
1 In fact the instance variable names of the superclasses too. See chapter ?? for a complete explanation.

-2001 Stéphane Ducasse ([email protected])


134 Using an Inspector

Figure 16.1: An inspector can be open using the halos (command click the morph) then click the red halo.

Figure 16.2: Selecting the inspect morph menu item proposed by the red halo.
3. Getting Information from an Object 135

Figure 16.3: An inspector can be open using the halos (command click the morph) then click the red halo.

instance variable is shown. In fact the right side uses information extracted from the inspected object while
the left uses information extracted from the class.

3 Getting Information from an Object


There are basically two ways to get information from an object: (1) by using an inspector or (2) by writing
small scripts and printing the result into the Transcript which should be opened before by dragging it from
the flaps or by executing the following expression Transcript open. 16.2 illustrates this approach.
Script 16.2 (Using the Transcript)

| flash |
flash := FlasherMorph newStandAlone.
flash openInWorld.
Transcript show: flash height printString ; cr.
Transcript show: flash width printString.
The same information and even more can be obtained using an inspector. Once you opened an inspector
as presented in 16.1 or using the halos, you have access to the list of instance variables of the inspected
object, by clicking on an element of this list in the left-pane (such as bounds, color, extension,
borderWidth . . . in figure 16.1), you get the value assigned to this instance variable. In fact on the right
pane you get the textual representation of the value. This is the result of sending the printOn: method to
the instance variable, similar to the print it menu. You can also see all the instance variables of an object
by selecting in the left pane on the all inst vars item that is just under self.

Important!
What is important to realize is that by doing so you are violating the way
a class chose to represent internally its state. That’s why an inspector
is a tool for debugging. This is not because a class represents a certain
state in a given way that you as a client will use it following this internal
representation. In fact you will interact with the objects by sending mes-
sages that are defined by their classes. This way classes protect you from
relying on their internal representation and the classes are free to change
later their internal representation without forcing you to change your own
code.

-2001 Stéphane Ducasse ([email protected])


136 Using an Inspector

Figure 16.4: Using an inspector to query the state of object. Here the method height, width and extent have
been invoked and they results were 25, 25 and 25@25.

An inspector allows us not only to look directly at the instance variables but also to send messages to
the inspected object. To send a message to the inspected object, type the expression you want to send into
the bottom pane and bring the menu to print or to do the expression. The variable self represents the
inspected object itself. The figure 16.4 shows that the methods height, width and extent have been
sent to the object.

Hint... Sometimes you do not know how to interact with an object. The first thing to do is to
look at the methods defined by its class and try. What you can also do is to open an inspector
to really look the value stored into an instance variable, then open a class browser and ask
all the places where an instance variable is accessed (click on the class, bring the menu and
select inst var refs). The system will open an list of methods that access the selected instance
variable. This way you can try to go from your understanding based on instance variables to
methods.

4 Changing Object State


When debugging an application it may be useful and necessary to correct the value of a certain instance
variable of a given object. An inspector helps you to change the values of instance variables. You can send
a message that modifies the value of the instance variable when such a message exists. The expression
self color: Color green in the figure 16.5 illustrates this possibility.
The other approach is to modify the value of the instance variable directly. Imagine that we want to
change the onColor of the inspected flasherMorph to blue. To do so, select in the inspector left pane the
instance variable you want to modify, here onColor in the figure 16.5, then on the right pane type the
expression that produces the new value (here Color blue), then bring the menu and select the accept
menu item.
Note that some objects may not have defined accessors for all their instance variables, so using mes-
sages can be impossible. Not defining methods for all instance variables is not a problem because it sup-
ports the protection of the object implementation choices, for example an instance variable may represent
conceptually several instance variables and the client of the class does not have to know it.

5 Objects with Implicit Instance Variables


When inspecting certain objects you may be intrigued by the fact that some objects do not have instance
variables as illustrated by the script 16.3 and the figure 16.6.
5. Objects with Implicit Instance Variables 137

Figure 16.5: Using an inspector to change the state of object: two approaches, sending a modifying message
or directly changing the value of the instance variable by using the accept menu item.

Figure 16.6: Inspecting a string. String have flexible lengths so they are implemented using implicit and
not named instance variables.

Script 16.3

’Squeak is cool’ inspect


This situation arises for certain objects such as strings or arrays that have a flexible size. Indeed having
named instance variables would not work because strings or arrays can have different lengths depending
on what we put inside. In such a case, the inspector just present the anonymous values of the inspected
object one after the other one.
Squeak offers the possibility to define class that do not have named instance variables but implicit name
variable by using a different message to create class (variableSubclass:... instead of subclass:...).

-2001 Stéphane Ducasse ([email protected])


138 Using an Inspector
Concept Summary 17
Browsers: Tools for
Reading and Editing
Code
Up until now you only program with the small environment dedicated to the turtle we made, the Micro
Browser. This browser is limited because you can only edit code for the class Turtle. In this chapter we
present the browsers that people use when developing in Squeak: the Class Browser and System Browser.
Note that we do not present all the functionality of these browsers but only the aspect related to defining a
method. In chapter ?? we will present how we can find methods and navigate the code.

1 The Micro Browser


The Micro Browser is a small browser that has been developed only for the purpose of helping novices to
program in Squeak. With the Micro Browser you could add, remove or save categories (group of meth-
ods) and define, remove or save single method. Figure17.1 explains the different information, while the
figure 17.2 presents the menu of the Micro Browser.
The Micro Browser is minimalist but this is on purpose. It represents the essence of a Smalltalk code
browser:
◦ you have categories that group elements, here method categories group methods) and
◦ you can define method and compile them using the menu Accept. The bottom pane allows you to read
but also edit the methods.
Note that Micro Browser does not allow certain operation such as removing key methods of the class
Turtle so that you cannot inadvertaly damage the system, so you may have less or slightly different
menus than the ones presented in 17.2.

-2001 Stéphane Ducasse ([email protected])


140 Browsers: Tools for Reading and Editing Code

Figure 17.1: The different elements of the Micro Browser explained. On the left the method categories,
on the right the methods belonging to the selected method category, and on the bottom the code editor that
allows one to define methods.

Figure 17.2: The menus of the Micro Browser explained.


2. Browsing one Class: the Class Browser 141

Figure 17.3: The class browser: a browser that lets you edit one single class.

Figure 17.4: The instance class buttons allows one to switch between messages sent to instances and to
classes. The ? button allows one to look at the class comment.

2 Browsing one Class: the Class Browser


The environment of Squeak proposes per default different code browsers to read and edit code. The simplest
one is class the Class Browser as shown by the figure 17.3. It is not present per default in the flaps but you
can use it by executing the following expression Browser newOnClass: Turtle if you want to
edit or browse the class Turtle.
One of the main difference between the Micro Browser and the Class Browser is that the latter has
much more functionality and that you can browse any class with it (see figure 17.7). Moreover, like all the
Squeak browsers, a Class Browser lets you browse all the messages that can be sent to instances such as
caro as well as all the messages that can be sent to classes such as Color or Turtle. Figure 17.4
presents the instance class buttons that allows one to switch between messages sent to instances and to
classes. Understanding clearly the difference between the two is the most complex aspect of Smalltalk that
we will explain in chapter ??. For now you can look at the code of classes like Color and browse the
messages sent to the class Color itself. However, pay attention that when you are defining a method that
the instance button is actived because 99.9% of the time you will be creating methods that corresponds to
messages sent to instances.
The Class Browser is quite close to the Micro Browser as shown by the figure 17.5 that shows how the
parts of the Micro Browser are placed in the Class Browser. The dashed arrow represents the flow: given
a class, we first have to select the method category, then select a method and we see the definition of the

-2001 Stéphane Ducasse ([email protected])


142 Browsers: Tools for Reading and Editing Code

Figure 17.5: The Micro Browser and the Class Browser are similar. The Class Browser has the same
elements that the Micro Browser: the method category, the methods, and the code editor.

method in the editor.


The figure 17.7 presents all the functionality offered by the Class Browser. Here we only describe
the most relevant ones and do not repeat them when menu are defined in different places. Please also
notice that as we did not introduce yet inheritance or left some concepts such as class variables, so certain
functionality certainly look mysterious to you and this is normal.
The convention with the menus is that if a name contains ... then you will be prompted to enter extra
information.

Class Menu. Obtained on the top left pane.

◦ browse full to obtain a System Browser (as shown figure 17.8).


◦ browse hierarchy to obtain a hierarchy browser (as shown in 20.3).
◦ fileOut to save the code of the class on the disk.
◦ show hierarchy to get a textual representation of the the hierarchy.
◦ show definition to get in the bottom pane the definition of the class.
◦ show comment to get in the bottom pane the comment of the class.
◦ inst var refs to get all the methods that references a given instance variable.
◦ inst var defs to get all the methods that assign values to a given instance variable.
◦ class refs to get all the places where the class is used.
◦ rename class to rename a class.
2. Browsing one Class: the Class Browser 143

Figure 17.6: Different information can be presented in the code editor (bottom pane of a browser). This is
set by the menu item what to show of the method menu. Pretty printing and coloring the code can help
you to distinguish the syntactic Smalltalk.

◦ remove class to erase a class from the system (dangerous).


◦ find method to show in the bottom pane the code of a method.

Method Category Menu. This menu offers all kind of functionality to manage categories. Some of the
options are also available from the previous menu. The most useful ones are:
◦ what to show to specify what you want to see in the editor pane (bottom pane) see Figure 17.6 for
all the options. Different information can be presented in the code editor such as the source or the
documentation. Particularly useful is the pretty printing and code coloring that help you to distinguish
the syntactic Smalltalk.
◦ fileOut to save all the methods defined in the category.
◦ new category to create a new category.
◦ rename/remove to rename or remove a category.

Method Menu. Obtained on the rightmost pane.


◦ senders of to get all the places where a message with the same name is used. This is an important
functionality that helps to understand how to use a method.
◦ implementors of to get all the classes where a method with a similar name is defined.
◦ inheritance to see all the methods with the same name defined in the class ancestors.
◦ versions to get all the versions of the methods. Useful when you want to go back to an old version.
◦ remove method to remove a method from the system (dangerous).

Editor Pane Menu. Obtained on the bottom pane, this menu offers all the traditional editing facilities
such as find, copy and paste. Some menu item requires that you select a piece of text while other work on
the complete editor.
◦ do it to execute the selected expression.
◦ print it to execute and print the result of the selected expression.
◦ inspect it to open an inspector on the result of the selected expression. Try Turtle new.
◦ explore it to open an explorer (a graphical inspector) on the result of the selected expression.
◦ debug it to debug the expression selected.
◦ accept to compile the method.

-2001 Stéphane Ducasse ([email protected])


144 Browsers: Tools for Reading and Editing Code

Figure 17.7: The Class Browser and all its menus.


3. The System Browser 145

Figure 17.8: The system or System Browser extends the Class Browser by adding two extra panes for the
class categories (groups of class) and for the class contained into a class category (second pane from the
left).

3 The System Browser


The class browser is interesting for beginners because it helps you to see only the code of one single class
and not get lost. However, the browser the most commonly used when developing in Squeak is the System
Browser or full browser. This browser is obtained from the main menu open followed by full browser.
As shown by the figure 17.8, the System Browser extends the Class Browser by adding two new panes
on the left for the class categories (groups of classes) and the classes. The idea is that with this browser we
can see all the code available in Squeak, as there is a lot of classes, they are grouped into class categories
(leftmost pane).
As shown by the dashed arrow in figure 17.9, to see or define a method we have to

1. select or create a class category, then the browser shows the classes that belong to this category (here
the category Caro-Turtle only contains the class Turtle),

2. select or created one class in the second pane, (here the class Turtle is selected), its method
categories are displayed in the third pane,

3. select or create a method category, the methods it groups are then shown in the rightmost pane,

4. finally select a method in the rightmost pane, then it definition is shown in the code editor (bottom
pane).

Note that per default, the browsers presents all the methods when no method category is selected.
The figure 17.10 presents all the menus associated with the different panes of a System Browser. We
only described the most important ones of the leftmost panes since the others have already been presented
previously in section 2.

Class categories

◦ find class to find a given class.

-2001 Stéphane Ducasse ([email protected])


146 Browsers: Tools for Reading and Editing Code

Figure 17.9: The elements of the Micro Browser are included into the System Browser. The dashed arrow
shows the flow of selection to read or define a method into a given class.
4. Defining a Method 147

Figure 17.10:

◦ recent classes to go back to one the previously visited class.


◦ fileOut to save the code of all the classes contained in the selecte category.
◦ add item to add a new class category.

4 Defining a Method
Defining a method using the Class Browser or the System Browser is similar that what you did with the
Micro Browser. First you have to select the method category in which you want to sort your method or
define a new category (see figure 17.11 and figure 17.12), then second once you have defined your method
in the bottom pane, you have to accept it (see figure 17.13). Note that you have topay attention that you are
in the class you want before defining your method. For this purpose you can use the item find class on the
left most pane (see figure 17.10).
.

-2001 Stéphane Ducasse ([email protected])


148 Browsers: Tools for Reading and Editing Code

Figure 17.11: Creating a new class category.

Figure 17.12: Entering the name of a class category.

Figure 17.13: Defining a new method.


Concept Summary 18
Implementing Joe the
Box

@@Should we change the class definition template@@


In this chapter you will program your first class, the class Box. Doing so you will learn how to describe
the behavior and the state of objects. We start with a first implementation that we later refine. This first class
is one of the first example used to teach object-oriented concepts to novices by researchers that invented
object-oriented programming.

1 Box’s Behavior and State


A box has the following behavior, it knows how to draw itself, move to a given distance, move to a given
point, rotate, grow and shrink. A typical scenario is described by script 18.1. A graphical result is shown
by the first figure of the chapter above.
Script 18.1 (A typical Box scenario)

| joe jane |
joe := Box new.
joe rotate: 15.
joe grow: 100.
joe move: 10@10.
joe moveTo: 150@200.
jane := Box new.
jane move: 30@-30.
jane shrink: 40.
jane rotate: 45
It is worth to spend some time looking at script 18.1. It shows that while at the first glance the scripts
may look similar ot the turtle messages they are not. From an object point of view, there is difference
between asking a turtle to draw a square as shown in chapter ?? and asking a box to draw itself. The

-2001 Stéphane Ducasse ([email protected])


150 Implementing Joe the Box

methods and the arguments that they require are different. Here a box knows how to grow, shrink and
rotate.
Before starting programming, we have to analyze the behavior of a box to imagine a possible way to
program it. Here is the behavior a box should have. A box should know how to:

◦ draw itself at a given location. When a new box is created it automatically displays itself.
◦ move to a given location (method moveTo: aPoint).
◦ rotate from a given angle (method rotate: anInteger).
◦ translate from a certain distance (method move: aPoint).

It is fundamental to start by looking at objects from their behavior. An object is a behavioral entity,
i.e., an entity reacting to messages. A similar behavior can be implemented by different manners so it is
crucial not to start to think in terms of the internal structures that may represent the object but in terms of
the essence of the object, its behavior.

Teacher’s Corner
Note the way we phrase the sentences describing box actions: we do not say the box is dis-
played but it displays itself. We always use the active form where the subject is the object
itself. Considering the object as a living being is a good way to think in an object-oriented
manner. Imagine talking about an animal or a person you will say that the person acts and not
is acted by others.
Teacher’s Corner
From behavior to state. Now from this description of the box’s behavior, we should imagine a possible
state for a box that could be used to implement the wished behavior. As this example and the concept of
box are familiar, we propose that boxe state is represented by a size, a position and a tilt.
In fact any box will be represented by such a triplet (size, position and tilt) but each given object will
have its own triplet values. For example, the box referenced by the variable joe in script 18.1 has its own
state, i.e., its own size, position, and tilt. In the same way the box jane has a similar state because it is
also a box created from the class Box too but it has its own state which may or not equal to the one of joe.
When the state of one given box changes it does not change the state of the other boxes. This situation is
illustrated by the first figure of this chapter. If this aspect is not clear we suggest you to (a) read chapter 15
and (b) create the class Box, inspect two instances and modify their states.

2 Defining the class Box


To create a class we use a dedicated browser called the system browser or class browser as presented in
chapter 17. To open such a browser, bring up the default menu and chose the menu item open... and the
item browser (or use the b).
To create a class, create first a new category (which represents a folder for all the classes we will create
related to this small project) by selecting the item add Item of the menu associated with the leftmost pane
of the browser (as explained in chapter ??). Name it for example JoeTheBox. When you select the newly
created category, the system displays a template to help you defining a new class (see class 19.1 and the
figure 18.1).
Class 18.1

Object subclass: #NameOfClass


instanceVariableNames: ’instVarName1 instVarName2’
classVariableNames: ’ClassVarName1 ClassVarName2’
poolDictionaries: ’’
category: ’JoeTheBox’
2. Defining the class Box 151

Figure 18.1: The browser shows you that a new class has been created by displaying it in the second pane
from the left.

Figure 18.2: The browser shows you that a new class has been created by displaying it in the second pane
from the left.

-2001 Stéphane Ducasse ([email protected])


152 Implementing Joe the Box

Modify the proposed template to obtain class 18.2 and in the bottom pane bring the menu and choose
the menu item accept. Now the class exists. The system shows you that the class is defined by displaying
it in the second pane as shown in figure 18.2. Using the terminology used in other programming languages
we can say that the class has been compiled. This means that we could already create instances of this
class, even if now this is not really useful since they do not have any specific behavior.
Class 18.2

Object subclass: #Box


instanceVariableNames: ’position size tilt ’
classVariableNames: ’’
poolDictionaries: ’’
category: ’Joe The Box’

Here are some explanations about the class definition 18.2: A box is a simple object. Hence, it is a
subclass Object (The concept of subclass and inheritance is explained chapter ??). The internal state of
box instances, such as the boxes joe and jane, is represented by instance variables of the class Box.
So line 2 we specify that the class Box has three instance variables by given their respective names. Here
the class Box has the instance variables position, size, and tilt. This indicates to the class Box
to create instances having three values representing the box’s state. As shown by class 18.2 we empty the
other parts of the templates because they are irrelevant for now.

Important!
A class acts as an object factory, an instance model, or a mould. The in-
stance variables describe the state that the instances created by the classes
will have. Each instance of the class will have the structure described by
the class but filled with its own values.

Instances have their own state but it follows the description given by the class. Two instances of the
same class can have different values for the same instance variable but they cannot have a different number
of instance variables.

Teacher’s Corner
The factory or mould metaphor is really useful to explain the difference between classes and
instances. Here, the class Box describes and creates boxes of different size, position and tilt
but all have these three characteristics.
Teacher’s Corner

3 Initializing Instances
Once the class is defined, create and inspect one of its instances by executing script 18.2 or by using an
inspector (see chapter 16). The figure 18.3 shows an inspector on a Box instance. All the instance variables
have nil as value. Indeed, when an instance is created by invoking the method new on a class, the default
behavior of the class is to return an uninitialized instance. Uninitialized means that all the instance variables
of the newly created instances have no value. To represent the no value concept, Smalltalk uses the special
object nil 1 . That’s why the instance variables of the inspected box have all as value nil.

Script 18.2 (Inspecting a box)

Box new inspect


3. Initializing Instances 153

Figure 18.3: Inspecting a non initialized box: all its instance variables are empty, i.e., having nil as value.

Figure 18.4: Inspecting an initialized box: all its instance variables contains some values coherent with
their role.

Having uninitialized values is not really good because methods may not work or have to test if the
variables have been correctly initialized. But even then this is not satisfactory because if an instance
variable is not initialized it is difficult to know the value to initialize it. In fact the best solution is to
initialize the instance as soon as it is created.
For that purpose we specialize the method initialize that sets up a default state for a box. The
method Box»initialize is automatically invoked by the method new on newly created instances. This
method sets the instance variables values. Once this method defined, in the bottom pane of the inspector
evaluate the expression self initialize. If you closed the inspector or want to convince you that
the method initialize is invoked when a new instance is created, reuse script 18.2 to check that the
created instance is now well initialized. In both cases, you should obtain a situation similar to the one
described by the figure 18.4.
Method 18.1

Box>>initialize
"A box is initialized to be in the center of the screen, with
50 pixels size and 0 tilt"

size := 50.
tilt := 0.
position := World bounds center

Reader Note
The fact that the new method automatically call the initialize method is a little extension
we added into Squeak. It may happen that such an extension will be included into Squeak at
the time you will read this book. In any case the plain Squeak solution to this problem is
explained in chapter ?? so that you can understand and program in Squeak without our little
extension.
Reader Note End
1 Nil comes from the latin nihil which means nothing.

-2001 Stéphane Ducasse ([email protected])


154 Implementing Joe the Box

4 Accessing Instance Variables


The method initialize above illustrates an important aspect of the object model of Squeak. The
instance variables are accessible from the methods as if they were defined in the method body. For example,
we are assigning 50 in the instance variable size. The instance variable size is accessible from any
method of the class Box.
Contrary to the of a script (| caro | for example) which do not exist after the script execution,
instance variables last the complete object life-time. We propose you some experiments to really understand
this phenomena below. Note that this behavior is not new, we used it constantly with the turtle. For example,
we changed the direction of the turtle using the method north which was somehow changing the internal
turtle state representing its direction, then later used the direction to perform some other actions.
We propose you to do some experiments to really understand this notion. Define the methods size
(method 18.2) which returns the value of the instance variable size and size: anInteger (method 18.3)
which changes the value of the instance variable size to be the one specified as argument. Such a kind of
methods are called accessor methods because they only get and set information in instance variables. The
code ŝize returns the value of the instance variable size.

In Squeak to return a value, use the character ˆ followed by the expression whose value
has to be returned.

Method 18.2

Box>>size

^size

Method 18.3

Box>>size: anInteger

size := anInteger

Now execute script 18.3 and use the menu item print it to get the results we present in script 18.3
using returns. If you have an inspector opened on a box instance, you can also execute the messages self
size, self size: 10 in the bottom left part of the inspector. Perform some other experiments to
prove yourself that you understand.
Script 18.3 (Instance variables life-time)

| joe jane |
joe := Box new.
joe size. returns 50
joe size: 10.
joe size. returns 10
joe size: 20.
joe size. returns 20
joe size: joe size + 5.
joe size. returns 25
jane := Box new.
jane size. returns 50
Now that you understand better instance variables, you should notice that instance variables are private
information of objects so creating accessor methods like the methodssize and size: above that only
5. Drawing a Box and Other Operations 155

access and return the value of an instance variable open the privacy of instances. We say that the break the
encapsulation of the object data. We will discuss in chapter ?? the use of accessors methods.
In summary we have:

Instance variables are accessible to all the methods of a class. Instance variables last
the same life-time than the object to which they belong to.

In Squeak, instance variables cannot be accessed from outside of an object. Instance


variables are only accessible from the methods of the class that define them.

5 Drawing a Box and Other Operations


Now that we initialize a newly created box using the method initialize, we are in position to define
methods without been worried about the initialization of instance variables.
During your experiments you may want to clear the screen. Use the script 18.4 for that purpose.
Script 18.4 (Clearing the screen)

World clearTurtleTrails

Method draw. We define the method draw that uses a turtle but we hide it as shown by method 18.4.
We create a method, put it at the right position of the box, set the direction of the turtle to the tilt of the
box, use the black color and then draw a square of the box size.
Method 18.4

Box>>draw
"Draw the receiver position in black"
"Box new initialize draw"

| aTurtle |
aTurtle := Turtle new hidden.
aTurtle jumpAt: position.
aTurtle turnRight: tilt.
aTurtle penColor: Color black.
4 timesRepeat: [aTurtle go: size.
aTurtle turnLeft: 90]

As soon as the method is defined and all the methods it calls exist, it is possible to invoke it. Test the
method by executing the code self draw into the bottom pane of an inspector in a similar way than
shown in figure 18.5, or by executing script 18.5.
Script 18.5 (sending the message draw to a box)

| joe jane |
joe := Box new.
joe draw

Experiment 18.1

Up until now, creating a new box did not displayed it. Change the method initialize so that any new
box is automatically displayed.

-2001 Stéphane Ducasse ([email protected])


156 Implementing Joe the Box

Figure 18.5: A problem with the grow: method. The box is not erased when it changes its size.

Method grow: The method grow: anInteger makes the box growing of a certain size and redraw
itself to reflect this size change. Propose a definition and use the inspector or dedicated scripts to tests your
method.

Method 18.5

Box>>grow: anInteger
"grow the receiver’s size from increment"

size := size + anInteger.


self draw

We propose the definition 18.5 for the method grow:. However, try script 18.6 to see that we have a
problem and propose a solution.

Script 18.6 (Problem with the first grow: method.)

| joe |
joe := Box new.
joe grow: 20.

joe grow: 40

The problem we have is that the turtle grows and redisplay itself well, but it does not remove the
previous box shape. To solve that problem we propose you to define a method named undraw which is
similar to the draw method except that it draw the box using a transparent color (method 18.6).
6. Limiting Duplication 157

Method 18.6

Box>>undraw
"erase the receiver"

| aTurtle |
aTurtle := Turtle new hidden.
aTurtle jumpAt: position.
aTurtle turnRight: tilt.
aTurtle penColor: Color transparent.
4 timesRepeat: [aTurtle go: size.
aTurtle turnLeft: 90]

Now that the method undraw is defined, the method grow: should call it before anything else as
shown by method 18.7.

Method 18.7

Box>>grow: increment
"grow the receiver’s size from increment"

self undraw.
size := size + increment.
self draw

To be able to execute the script we presented at the beginning of the chapter, implement the methods

◦ move: aPoint which translate the box from a distance in x and y specified as a point.

◦ moveAt: aPoint which move the box to the specified point.

◦ rotate: anInteger which rotates the box of a given angle.

◦ grow*: anInteger and shrink*: anInteger that make grow and shrink the receiver by
a given factor.

6 Limiting Duplication

The methods draw (method 18.4) and undraw (method 18.6) are nearly the same except for the color of
the turtle. This is not really good, since every times we will change one method we will have to change the
other and there is chance that we forgot. Note that on this really simple example this is not really a problem
but we want to show you a good principle.
Propose a solution to this problem. The idea is that to avoid duplication, the methods draw and
undraw can call a third method with the color of the pen as argument. We could named this method
drawWithColor:. Try to implement such a method before reading the solution.
The drawWithColor: aColor (method 18.8) shows a possible implementation, it factors out the
duplicated code. Now change the methods draw and undraw to call this method with the right argument.

-2001 Stéphane Ducasse ([email protected])


158 Implementing Joe the Box

Method 18.8

Box>>drawWithColor: aColor
"Draw the receiver using a given color"

| aTurtle |
aTurtle := Turtle new hidden.
aTurtle jumpAt: position.
aTurtle turnRight: tilt.
aTurtle penColor: aColor.
4 timesRepeat: [aTurtle go: size.
aTurtle turnLeft: 90]

In general we should avoid as much as possible to have duplicated code. This is not a problem to
duplicate code for a small experiment. However, if you want to keep the code always think that you should
create other method to share and reuse the duplicated code. Creating one or several methods to factor the
duplicated code is a good trick to cure duplicated code.

Avoid duplicated code. Refactor the duplicated code by calling a method representing
the duplicated code.

7 Looking at Alternate Designs


We said that the implementation we proposed is one of the multiple ways of implementing the behavior of
the Box class. In this section we want to look at other possible implementations. First let us analyze the
current implementation. The class Box has its own state via the instance variables tilt, position, and
size then gives a part of its state to a turtle, its tilt and position. The Box class uses the Turtle class to
realize its behavior. This is a common practice where a class do not repeat behavior but reuse the behavior
of an existing class.
We used the class Turtle because it was familiar to us. However, another class, the class Pen could
have been a possible candidate too. Look at the class Pen and change the method drawWithColor: to
use it instead of Turtle. What is important is that the interface proposed by the class Box should not
changed. We are changing the internal implementation of the class Box but this should not change its
behavior.
Now if we look carefully we see that the turtle or the pen instance are created every time the box is draw
and undraw. In addition the state of the box is systematically copied to the turtle state then lost because a
turtle is recreated and the previous one is lost. One idea would be to use a turtle as representing part of the
box state. Indeed a turtle has also a position and a tilt.
Define the class BoxT as shown below in class 18.3 and reimplement some of the box’s methods to
convince you that this is possible. This solution has the advantages that less objects are created, less state
is copied from the box to the turtle and as drawbacks that the class Box is tied with the class Turtle.
Class 18.3

Object subclass: #BoxT


instanceVariableNames: ’size turtle’
classVariableNames: ’’
poolDictionaries: ’’
category: ’Joe The Box’

To help you we show two methods, method 18.9 and method 18.10, that are important. Try to do it by
yourself first. Implement all the other methods.
8. What you should have learned 159

Method 18.9

BoxT>>initialize
"A box is initialized to be in the center of the screen, with
50 pixels size and 0 tilt"

size := 50.
turtle := Turtle new hidden.
turtle jumpAt: World bounds center.

Method 18.10

BoxT>>drawWithColor: aColor
"Draw the receiver using a given color"

turtle penColor: aColor.


4 timesRepeat: [turtle go: size.
turtle turnLeft: 90]

8 What you should have learned


Blabla here...

-2001 Stéphane Ducasse ([email protected])


160 Implementing Joe the Box
Concept Summary 19
The Baby Clicking
Game

@@ I should stress the incremental process and the fact that an object can automatically react to the
methods as soon as they are compiled @@
In this chapter we propose you to build a small game for baby that learns how to use a mouse. The idea
is to have a flashing morph moving on the screen and to click on it to change its direction. This way we
will show you how to change the behavior of a morph and how to add interaction with the morph. Squeak
defines a simple flashing morph, called FlasherMorph that we will extend as we do not want to rewrite
it. Doing so you will learn how you can define a class by refining another one and extending its behavior.
This example will be then analyzed in the next chapter to explain you what is inheritance, i.e., how can we
extend or refine the behavior of a class to obtain other classes with related behavior. The next chapter will
answer all the questions that this chapter will raise. Just play the game and follow the instructions for now.

1 A Flashing Morph
We do not want to create a Morph from scratch because this is too complex. Indeed morphs know how to
display themselves, get transformed, change color, to react to external events,... In fact what we will do is
reuse a class that already implements what we need. For this purpose we will extend the FlasherMorph
class. A FlasherMorph is a simple morph that flashes, i.e., changes its color at constant rate. The
script 19.1 shows how to create such a simple morph. Note that Morphs are created with a slightly different
creation method. Indeed to create an instance of a Morph we should use the method newStandAlone
and not the method new as we used it for the class Turtle. This is because Morph is a class that requires
special treatment. The method new is the normally the method that should be sent to a class to create new
instances.
Script 19.1 (Creating a flashing Morph)

FlasherMorph newStandAlone openInWorld

-2001 Stéphane Ducasse ([email protected])


162 The Baby Clicking Game

Figure 19.1: Bring up the menu over the leftmost pane of the browser and select the add item choice to
create a new class category.

2 Defining the class EscapingFlasher


To create a class we use a dedicated browser called the system browser . To open such a browser, bring
up the default menu and chose the menu item open... and the item browser (or use the b) as shown in the
figure 19.1.
To create a class first we create a new category (which represents a folder for all the classes we will
create related to this small project) by selecting the item addItem of the menu associated with the leftmost
pane of the browser (Figure 19.1). Name it for example Moving Flasher. When you select the newly
created category, the system displays a template to help you defining a new class (see class 19.1 and the
figure ??).
Class 19.1

Object subclass: #NameOfClass


instanceVariableNames: ’instVarName1 instVarName2’
classVariableNames: ’ClassVarName1 ClassVarName2’
poolDictionaries: ’’
category: ’Moving Flasher’

Modify the proposed template to obtain class 19.2 and in the bottom pane bring the menu and choose
the menu item accept as shown by the figure 19.2. Now the class exists. The system shows you that the
class is defined by displaying it in the second pane as shown in figure 19.3. Using the terminology used
in other programming languages we can say that the class has been compiled. This means that we could
already create instances of this class, even if now this is not really useful since they do not have any specific
behavior.
Class 19.2

FlasherMorph subclass: #EscapingFlasher


instanceVariableNames: ’’
classVariableNames: ’’
poolDictionaries: ’’
category: ’Flasher’
2. Defining the class EscapingFlasher 163

Figure 19.2: Once you edited the class definition template, you compile the class by using the menu accept
in the bottom pane.

Figure 19.3: The browser shows you that a new class has been created by displaying it in the second pane
from the left.

-2001 Stéphane Ducasse ([email protected])


164 The Baby Clicking Game

3 A Moving Flasher
The following step to obtain a moving morph is to make the morph moves without intervention of the user.
The Morphic system supports the animation of morphs and we will use it to make our morph moving. The
step method is called by the system every amount of time determined by the stepTime method. The
class FlasherMorph uses this mechanism to regularly change its color.
So in addition to the color change performed each step, we will change the position of the Morph
in diagonal. That’s why the method step first does a super step. This means that we ask that the
behavior of the step method defined on the class FlasherMorph to be executed then we change the
position by adding 2 in x and 2 in y to the current position of the morph (by using adding to the position a
point). This approach is explained in detail in the next chapter. Define the method step as shown in the
method 19.1.
Method 19.1

EscapingFlasher>>step
"At each step change the position of the morph by adding a constant
distance represented by a point"

super step.
self position: self position + (2@2)

Now execute the script 19.2, your morph should be moving.

Script 19.2 (Creating an escaping flasher)

EscapingFlasher newStandAlone openInWorld

4 Interactively Changing the Direction of Flasher


Now that the morph is moving we would like to be able to let a baby to click on it to change its direction.
Morph allows one to specify a lot of different interactions. We do not want to go on the details but showing
some examples. The basic idea is that certain methods should define when the morph wants to be aware
of certain events such as pressing or releasing the mouse button on it or entering or leaving it. Then the
morph has to specify the behavior associated with the kind of event it is interested in.
Let us look at a concrete example. For our game we want to know when the mouse button is pressed,
for that we specify the method handlesMouseDown: to say that the morph wants event generated by
pressing the mouse button (method 19.2) and the method mouseDown: to specify how it behaves when it
receives such an event.
Method 19.2

EscapingFlasher>>handlesMouseDown: evt

^ true

Now we specify the method mouseDown: (see method 19.6). This method is invoked each time the
mouse is pressed on a morph (if the method handlesMouseDown: specifies by returning true that the morph
is interested into that kind of event). Right now we just print something in the Transcript to be sure
that the morph reacts when we click on it. open a Transcript by dragging one from the left flap, create
a new instance of EscapingFlasher and click on it.
4. Interactively Changing the Direction of Flasher 165

Method 19.3

EscapingFlasher>>mouseDown: evt

Transcript show: ’ mouse down’ ; cr.


Smalltalk beep

Our goal is that when we click on the morph it should change its direction. So we should think about
how can we represent this behavior. Different approaches exist, the first one that came in our mind is that
we just have to be able to change the point that is added in the step method. To change the direction, we
just have to negate the point. By adding a negated point the flasher will go up instead of down. What we
need is a way to know if the last point added was positive or negative. When the morph is clicked we just
have to revert this information. Even simpler if we store a point representing the last point added to the
position we just have to negate it each time the morph is clicked. So we will add an instance variable to the
EscapingFlasher class so that each instances will now know the last point added. Modify the class
EscapingFlasher according to class 19.3.

Class 19.3

FlasherMorph subclass: #EscapingFlasher


instanceVariableNames: ’point’
classVariableNames: ’’
poolDictionaries: ’’
category: ’Flasher’

When an instance is created the values of its instance variable is nil, which is the object that represents
nothing (see Chapter ?? for a more detailled description). So the instance variable point of the instance
of EscapingFlasher will be nil while we would like it to be 2@2 to obtain the same behavior that
the one of the method method 19.1. Verify this situation by creating an instance as shown in script 19.2.
We have to find a way to specify that the value of the instance variable point should be 2@2. This can be
done by specializing the method initializeToStandAlone which is called when a Morph is created.

Define the method initializeToStandAlone that is called by the method newStandAlone


each time a new morph is created as shown by method 19.4.

In the method EscapingFlasher »initializeToStandAlone, we want to let the morph


initialized itself as it is done by its superclass then initialize the point. ThatÕs why we use the expression
super initializeToStandAlone as first line of method 19.4.

Method 19.4

EscapingFlasher>>initializeToStandAlone

super initializeToStandAlone.
point := 2@2.

For a baby the default size of the flasher is too small so we propose you to take the advantage of
initializing the flasher to change its size. We also would like to distinguish it from the default red flasher,
so we change its color to be blue. Try method 19.5

-2001 Stéphane Ducasse ([email protected])


166 The Baby Clicking Game

Method 19.5

EscapingFlasher>>initializeToStandAlone
"self newStandAlone openInWorld"

super initializeToStandAlone.
self bounds: (self bounds scaleBy: 3).
self color: Color blue.
point := 2@2.

Now changing direction is just negating the point each escaping flasher morph holds. Define the method
changeDirection as described in method 19.6. Here this method adds a specific behavior to the class
EscapingFlasher and does not hide or override any behavior of the superclass.
Method 19.6

EscapingFlasher>>changeDirection

point := point negated

Redefine the method mouseDown: to call the method changeDirection as shown in method 19.7.
Method 19.7

EscapingFlasher>>mouseDown: evt

Smalltalk beep.
self changeDirection

Hot Pepper
Note that this method overrides1 the method defined handlesMouseDown: in one the su-
perclasses of EscapingFlasher, it just replaces this behavior for the class Escaping-
Flasher. The methods step and handlesMouseDown: are used in two different ways:
the first extends the behavior while the second replaces it.
Hot Pepper End
During a first reading of the book, you can skip the rest of this chapter and start to read the next chapter
that explains the fundamental aspects of inheritance using the EscapingFlasher as example. The rest
of this chapter adds new features to the EscapingFlasher and shows how keyboard events can be handled.
Nothing really conceptual is explained.

5 Accelerating Flasher
Any good game should stress a bit its player else it quickly starts to get bored. So we would like our
escaping flasher to accelerate each time we click on it. To still let a chance to the player to get it we would
like the following behavior: clicking on it should accelerate it if the morph is not at its maximum speed.
If this is the case, clicking should slow it down to its original speed. Hence, the game will be to click fast
when the flasher is accelerating until it is getting slowly again.
Now we will just change the class EscapingFlasher itself by adding new methods and new state
representing the acceleration. However, object-oriented programming lets you define classes incrementally
1 it does not invoke the behavior of the method handlesMouseDown: defined in the superclass – This will be explained in the

next chapter
5. Accelerating Flasher 167

using inheritance, i.e., where you only specify the differences between the class from which your new
class inherits. We used this technique when we defined the class EscapingFlasher. We could create
a new subclass of the class EscapingFlasher but we will let you that as an exercise. Here we modify
directly the class EscapingFlasher as follow. We add an instance variable naed increasing that
will represent if the speed of the flasher is increasing or not. This way we will know if we should continue
to accelerate it or not.
Class 19.4

FlasherMorph subclass: #EscapingFlasher


instanceVariableNames: ’point increasing ’
classVariableNames: ’’
poolDictionaries: ’’
category: ’Flasher’

As we consider that the morph behavior is to start accelerating when it just got created, we should
specify that the value of the instance variable increasing is true. true represents the true value (see
chapter ??). That is why we modify the method initializeToStandAlone to initialize correctly the
value of the instance variable increasing to true.
Method 19.8

EscapingFlasher>>initializeToStandAlone

super initializeToStandAlone.
point := 2@2.
self bounds: (self bounds scaleBy: 3).
self color: Color blue.
increasing := true

As we want that the escaping flasher accelerates when we try to catch it, we should do some action
when the mouse passes over an instance. For this purpose we define the method handlesMouseOver:
to return true, which means that the receiver is interested in receiving events when the mouse passes over
it.
Method 19.9

EscapingFlasher>>handlesMouseOver: evt
"return true to receive event when the mouse is over the receiver"

^ true

Now that we said that the morph will receive events we have to define its behavior when receiving
them. For this purpose we specialize the method mouseEnter: as defined in method 19.10. The idea
is that if the morph is accelerating we will speed it up a bit more else it will be slowing down so we will
continue to slow it down.
Method 19.10

EscapingFlasher>>mouseEnter: evt

increasing
ifTrue: [self speedUp]
ifFalse: [self slowDown]

-2001 Stéphane Ducasse ([email protected])


168 The Baby Clicking Game

The method slowDown can be defined as follow: it checks that the speed (the point we are adding at
each step) is not too small (line 1) . If this is the case, this means that we reach the limit of slow speed
so we specify that from now the morph should speed up again (line3). if the speed is still bigger than the
slowest speed we continue to decrease its value (line 4).
Method 19.11

EscapingFlasher>>slowDown

point < (5@5)


ifTrue: [increasing := true]
ifFalse: [ point := point - (2@2)]

The method speedUp implements the same logic but regarding the fastest speed. First we check if the
fastest limit has not been reached. If this is the case we specify that from now the morph should slow down
(line 4), else we continue to increase the speed of the morph (line 5).
Method 19.12

EscapingFlasher>>speedUp

point > (20 @20)


ifTrue: [increasing := false]
ifFalse: [point := point + (5@5)]

Try now your morph it should first accelerates then slow down when you move the mouse on it.

Helping yourself. If you are a bit puzzled by our solution you can redefine the methods speedUp and
slowDown to include a trace and open a Transcript to follow the program trace. method 19.13 shows a
way to produce a trace. Do the same for the method speedUp and analyze the trace.
Method 19.13

EscapingFlasher>>slowDown

Transcript
show: ’Entering method slowDown: increasing is: ’ ;
show: increasing printString;
show: ’ speed is: ’ , point printString ; cr.

point < (5@5)


ifTrue: [increasing := true]
ifFalse: [ point := point - (2@2)]

6 Handling Keyboard Events


Parents are usually less good than kids to play game but they are better at typing keyboard. So we want to
have a mode where nervous parents can stop the morph and reset its speed. Doing so you will learn how a
morph handle keyboard events.
Define a subclass of the EscapingFlasher if you want to keep its behavior separate from the other
experiments. The following assumes you did so. Handling keyboard is slightly more complex than mouse
event. It requires two extra methods. First, we specify that the morph wants to get keyboard events by
6. Handling Keyboard Events 169

defining the method handlesKeyboard: to return true. Then we define the method keyDown: to
handle the key and perform associate action.
Method 19.14

EscapingFlasherWithKeyboard>>handlesKeyboard: evt
"Indicate by returning true that the receiver handle keyboard events"

^ true

Using the method keyCharacter, the method keyDown: accesses information holds by the event
to know which key has been pressed. This method returns a character. Characters are represented by on
dollar sign followed by a letter such as $a, $1, or $.. Obtaining inivisible characters such as space or
tab is simply done by asking the class Character as follow: Character space, Character tab,
or Character cr to obtain a carriage return character (see Chapter ?? for more detail). The method
keyDown: invokes different actions depending on the key pressed. Change it to see the effect. If you are
curious to see what is a event you can also try to add a case with anEvent inspect. This will open an
inspector on the event.
Method 19.15

EscapingFlasherWithKeyboard>>keyDown: anEvent
"Handle a key down event."

| char |
char := anEvent keyCharacter.
char = $a
ifTrue: [self resetSpeed].
char = $q
ifTrue: [self slowDown].
char = $s
ifTrue: [self stop].
char = $l
ifTrue: [self startStepping].

Now if you try you will see that what the characters you are typing are printed in the browser. The
problem is that the focus of the mouse is not given to the morph. To assign correctly the focus, define the
methods mouseEnter: and mouseLeave: as follow.
Method 19.16

EscapingFlasherWithKeyboard>>mouseEnter: evt

evt hand keyboardFocus: self

Method 19.17

EscapingFlasherWithKeyboard>>mouseLeave: evt

evt hand releaseKeyboardFocus: self

Now you can implement the functionality you want to associate with the keys. For example, the method

-2001 Stéphane Ducasse ([email protected])


170 The Baby Clicking Game

resetSpeed can be defined as shown by method 19.18, we simply reset the size of the added point to its
original value.
Method 19.18

EscapingFlasherWithKeyboard>>resetSpeed

point := 2@2.

The methods stop and startStepping are defined on the class Morph. stop stops the animation
of the morph, i.e., the step method is not invoked anymore and startStepping does the opposite.
We could improve the code by defining a method named defaultSpeed returning the point 2@2 and
to use it everywhere we would need to initialize the speed. So such a method would have to be defined into
the class EscapingFlasher. Note that while a system evolves the code changes and it is frequent to
have to adapt or refactor the code of classes to get a better system. A solution is only good for the problem
it solves today, tomorrow we may have another slightly different problem that will require to modify our
solution.
Concept Summary 20
Inheritance
@@The name of the flasher is not stable yet...FlasherMorph in the distribution but Flasher in the changeset
of scott@@
In the previous chapter we show you that we can develop a class, the EscapingFlasher but just
defining some specific methods and reusing the behavior of another class, the class FlasherMorph. This
was possible because we used a mechanism called inheritance which is a central concept in object-oriented
programming.
The term inheritance represents the idea that a class inherits behavior from its ancestor but also that it
can changes it to fit its own need in certain limits. Inheritance allows one to specify new class by refining
the behavior of ancestor classes. Pratically in Smalltalk a class that inherits from an ancestor can add new
behavior, change existing one or extend ancestor behavior. Note that the separation between the different
sort of inheritance is just a way for us to show you the different possibilities. There is nothing in Smalltalk
that prevents you to mix them, which is often the case when implementing real classes. We now look at
these aspects and show you how inheritance works. We first present some vocabulary then look at the
inheritance of instance variables and finally explain the inheritance of behavior via the lookup of methods.
For this purpose we will also explain how messages sent to an instance are looked up in the class of
the instances.Together with the chapter ?? this chapter represents the core of object-oriented programming.
Be sure that you really understand well this chapter, if necessary read again the preceding chapter that is
illustrating the concept without explaining them.

1 Inheritance Foundation
The idea behind the inheritance mechanism is that oftentimes we want to define a class whose behavior is
nearly the same as one class that already exist. In the previous chapter, we wanted to have a morph flashing
and moving while a class with a morph flashing was already defined in the system. Without inheritance
we could copy the complete class and define a new one. However such a practice is not good because we
would have duplication of the code. This means that if there is an error in one the class it will be copied in
all the other classes. Moreover if we would copy the code of all the ancestors we would have to look at all
the methods of the class to understand the different behavior the new class defines. Inheritance was then
introduced to support the incremental definition of class. Inheritance lets a programmer say that he would
like nearly the same behavior that a given class but with some changes, extensions or refined behavior.

Vocabulary. A class inheriting from an ancestor class is called a subclass of that ancestor. The ancestor
is called the superclass of the class. The class FlasherMorph is the superclass of the class Escaping-
Flasher. EscapingFlasher is a subclass of FlasherMorph. In object-oriented methodology
inheritance graphically is represented using an arrow with a triangle, the triangle been glued to the super-
class. The fact that the class EscapingFlasher is a subclass of FlasherMorph is depicted in the
figure 20.1.
The left diagram in figure 20.1 shows the inheritance relation betwen the two classes. Usually we want
also to show more information such as the added instance variables and the methods. To stress that a given
method is redefined in a subclass we can also make it apparent in the diagram such as shown in the right in
figure 20.1.

-2001 Stéphane Ducasse ([email protected])


172 Inheritance

Figure 20.1: Graphical way of representing inheritance. A simple form on the left and a detailed one on
the left.

In Smalltalk an ancestor class can have mutiple descendant classes but a class can only have a single
parent. This is why the inheritance mechanism is qualified as single inheritance. In Smalltalk each class
inherits from another one to the exception of the root of the ancestor: the class Object. The root of all
the ancestor is the class Object. Object defines all kinds of useful behavior we want to inherit such
as the error handling, the possibility to add breakpoint in the code, and other functionality required by the
environment. A class inherits from the class Object at least. This means that if you do not know from
which class a new class should inherit, you should specify that your class inherits from Object.
The fact that each class should inherit from another one is represented by the way classes are created.
The following class definition shows that the superclass FlasherMorph is asked to be the superclass
of the newly created class EscapingFlasher. Note that we put a hash sign # before the name of the
new class, here #EscapingFlasher because we only give the name of the class as the class does not
exist yet. We do not put a # for the ancestor, here FlasherMorph because we ask the class itself which
already exist to create its subclass.
Class 20.1

FlasherMorph subclass: #EscapingFlasher


instanceVariableNames: ’’
classVariableNames: ’’
poolDictionaries: ’’
category: ’Flasher’

2 The Hierarchy Browser


When displaying the code of a class, the system browser only shows the methods and instance variables that
are locally defined, i.e., the changes compared with the class ancestors. In some situation this is important
to see the ancestors as well as the class currently defined or under scrutiny. For this purpose the Squeak
environment contains the so called, hierarchy browser. To get a hierarchy browser you can use h or as
shown by the figure 20.2.
As shown in figure 20.3, the hierarchy browser is divided in three horizontal layer:
3. Instance Variable Inheritance 173

Figure 20.2: Opening a hierarchy browser from the system browser.

Figure 20.3: Browsing the inheritance tree from the class EscapingFlasher.

1. the first pane simply shows the category of the main class displayed. In figure 20.3 we have the
category Flasher while in figure 20.4 we have the category Morphic-demo because this is where the
class FlasherMorph is defined.

2. The second layer is much more interesting. In particular the first pane that shows the inheritance
chain of the selected class. In figure 20.3 we have the inheritance chain of the class Escaping-
Flasher while in figure 20.4 we have the inheritance chain of FlasherMorph. Note the dif-
ference between the two: the hierarchy in figure 20.4 is showing the two subclasses of the class
FlasherMorph because it shows the inheritance tree of this class. The second and third panes are
the method categories and the methods as in a normal browser.

3. The third pane as usual shows the code of the selected class or method.

You can see the code of the classes by simply clicking on a class in the inheritance chain shown in the
first pane of the second layer. The browser will then automatically shows you the class definition and the
methods of the selected class.

3 Instance Variable Inheritance


The inheritance of instance variables is simple: Instances of a class have all the instance variables defined
in that class plus the ones defined in the superclasses of that classes. The name of an instance variable

-2001 Stéphane Ducasse ([email protected])


174 Inheritance

Figure 20.4: Browsing the inheritance tree from the class FlasherMorph. The hierarchy
browser shows that the class FlasherMorph has two subclasses: EscapingFlasher and
EscapingMorphWithKeyboardControl @@to be changed if flasher is integrated into the sys-
tem@@.

Figure 20.5: Instances of the class EscapingFlasher have the instance variables defined in
the class EscapingFlasher and the ones defined in the superclasses of EscapingFlasher,
i.e., FlasherMorph and its superclasses (not shown here). This means the instance variables:
point and increasing plus onColor defined on the class FlasherMorph, borderWidth and
borderColor on the class BorderedMorph, and bounds owner submorphs fullBounds
color, and extension defined on the class Morph.
4. Method Inheritance and Method Lookup 175

should be unique amongst the superclass chain, i.e., if a class defines a given instance variable bounds, a
subclass cannot define another instance variable with the same name.
The figure 20.5 shows the instance variables of the classes EscapingFlasher and Flasher-
Morph.

Example. Instances of the class EscapingFlasher have the instance variables point and increasing
defined in the class EscapingFlasher plus the ones defined in the superclasses of EscapingFlasher,
i.e., onColor defined on the class FlasherMorph, borderWidth and borderColor on the class
BorderedMorph, and bounds owner submorphs fullBounds color, and extension defined
on the class Morph. Note that we can ask the class itself for all its instance variable names as follow:
Script 20.1 (Asking a class all its instance variables)

EscapingFlasher allInsVarNames.
returns
#(’bounds’ ’owner’ ’submorphs’ ’fullBounds’ ’color’ ’extension’
’borderWidth’ ’borderColor’ ’onColor’ ’point’ ’increasing’)
Some other rules exist for the less common variables such as classVariables or indexed instance vari-
ables reader willing to know the complete Smalltalk inheritance semantics should refer to chapter ??.

Designer Hints
You should pay attention that most of the time we are not interested in knowing the inherited
instance variables. We just want to know that we are defining an instance variable with a name
already used in a superclass, but the browser notifies us when such a situation arises.
We are not interested by the inherited instance variables because instance variables represents
the internal representation of object behavior. We should not make any assumption about the
internal coding of a given behavior, because it may change and we do not want that our own
code break if such an internal representation changes.
Designer Hints

4 Method Inheritance and Method Lookup


Besides inheriting the state of an ancestor a descendant also inherits its behavior and can modify it. To
understand how behavior inheritance works we have to present what’s happen when an object receives a
message. When an object receives a message the corresponding method should be found. This is phase is
called the method lookup. Let us look at the way method lookup works.

Reacting to a message reception. When an object receives a message the lookup for a method with the
same selector start in the receiver class. If the method is found in the class it is executed (see figure 20.6),
else the lookup continue in the superclasses (see figure 20.9). If the method is found it is executed. If the
lookup fails on the class Object, the root of the inheritance, this means that the method is not defined for
this receiver and an error is generated and the method doesNotUnderstand: is sent to the receiver –
in such a case a dialog box pops up and ask us if we want to open a debugger.

Examples. As shown in the figure 20.6 when an instance of the class EscapingFlasher receives the
message speedUp, the lookup of the method that will be executed starts in the class of the receiver, here
EscapingFlasher. The method is defined in this class so it is executed. As shown in the figure 20.9
when an instance of the class EscapingFlasher receives the message onColor: Color red,
the lookup of the method onColor: starts in the class of the receiver, here EscapingFlasher. The
method is not defined there so it continues in the superclass of EscapingFlasher, the class Flasher-
Morph where the method onColor: is defined.

-2001 Stéphane Ducasse ([email protected])


176 Inheritance

Figure 20.6: An instance of the class EscapingFlasher receives the message speedUp. The lookup
of the method that will be executed starts in the class of the receiver, here EscapingFlasher. The
method is defined in this class so it is executed.

Added methods on subclasses. The method lookup explains the way methods are found when a message
is sent to an instance of a particular class. It also means that while a subclass inherits the behavior of its
ancestors the opposite is not true. This is not because a subclass defines a new method that an instance of
its superclass can understand it. As shown by the figure 20.8 an instance of the class FlasherMorph
receives the message speedUp. The method lookup starts in the class of the receiver here Flasher-
Morph. The method is not defined in this class so it continues in the superclasses and eventually generates
an error since the method is only defined in the class EscapingFlasher.

Important!
When an object receives a message the lookup for a method with the same
selector start in the class of the receiver.

5 Hiding Superclass Method

As the method lookup starts in the class of the receiver it is possible to redefine the definition of a given
method in a subclass. We just have to define a method with the same name as a method defined in a
superclass of the subclass. Such a practice is called method hidding or method redefinition. This practice
is commonly used to let a subclass defines its own specific behavior. For example the class Morph defines
the method handlesMouseDown: as a complex expression that will return false to indicate that per
default a morph is not interested to receive events when we are clicking on it (see method 20.1). As we
need to know when we are clicking on an escaping morph, we define on the class EscapingFlasher
the method handlesMouseDown: so that it returns true (see method 20.2).
5. Hiding Superclass Method 177

Figure 20.7: An instance of the class FlasherMorph receives the message speedUp. The method
lookup starts in the class of the receiver here FlasherMorph. The method is not defined in this class so
it continues in the superclasses and eventually generates an error.

Figure 20.8: An instance of the class EscapingFlasher receives the message zork. . The method
lookup starts in the class of the receiver here EscapingFlasher. The method is not defined in this class
so it continues in the superclasses. Since zork is not defined it eventually generates error.

-2001 Stéphane Ducasse ([email protected])


178 Inheritance

Figure 20.9: An instance of the class EscapingFlasher receives the message onColor: Color
red. The lookup of the method onColor: starts in the class of the receiver, here EscapingFlasher.
The method is not defined in such a class so it continues in the superclass of EscapingFlasher, the
class FlasherMorph where the method onColor: is defined.

Method 20.1

Morph>>handlesMouseDown: evt

self eventHandler ifNotNil: [^ self eventHandler handlesMouseDown: evt].


^ self allowsGestureStart: evt.

Method 20.2

EscapingFlasher>>handlesMouseDown: evt

^ true

The figure 20.11 shows that an instance of the class EscapingFlasher will execute a different
method handlesMouseDown: than an instance of the classes Morph or FlasherMorph because two
different methods with the same exist in different classes and that the lookup starts in the class of the
receiver then continues in the superclasses of this class.

6 The Dynamic Aspect of Self Sends


We always said that self represents the receiver of the message. This has a subtle but really powerful
consequence. Even if self is used in a method found in a superclass of the receiver class, it represents
the receiver. This implies that all the messages sent to self will be looked up starting from its class.
The figure ?? illustrates this point in an abstract way, a concrete example follows. Imagine the class
Object defines the method yyy that sends the message xxx on its receiver. Then imagine that the class
6. The Dynamic Aspect of Self Sends 179

Figure 20.10: Method hidding or redifinition. An instance of the class EscapingFlasher executes a
different method handlesMouseDown: than an instance of the classes Morph or FlasherMorph.

EscapingFlasher defines a method xxx (for this example this is not important that Object defines
also a method xxx which should be the case in practice). When an instance of the class

Important!
self send, i.e., messages sent to the variable self are always looked
up by starting from the class of the receiver and continuing in its su-
perclasses.

A Real Example. Let us illustrate this important point. There is no direct example in the flasher example
you defined in the previous chapter so add new functionality to show you how this important aspect works.
We suggest you to type the following methods to see the results. script 20.2 opens an escaping morph,
an inspector on it, and print the texttual representation returned by the morph, here something like ’an
EscapingFlasher(191)’.

Script 20.2

| esc |
esc := EscapingFlasher newStandAlone.
esc openInWorld.
esc inspect.
esc printString

Imagine we want to improve the information that is printed when we do a print it of an escaping morph
or when we click on the self item in an inspector. There is an easy way to do that we redefine the method
printOn: aStream as follow:

-2001 Stéphane Ducasse ([email protected])


180 Inheritance

Figure 20.11: Method hidding or redifinition. An instance of the class EscapingFlasher executes a
different method handlesMouseDown: than an instance of the classes Morph or FlasherMorph.
6. The Dynamic Aspect of Self Sends 181

Figure 20.12: self is dynamic because it always represents the receiver of the message. This means that
all the message sends sent to self are looked up by starting in the class of the receiver.

Method 20.3

EscapingFlasher>>printOn: aStream

aStream nextPutAll: ’I am an instance of ’ , self class name.


aStream nextPutAll: ’ and my color is: ’, self color printString

Now when you send the message printString or click on the self item of an inspector you
should get something like I am an instance of EscapingFlasher and my color is:
Color red

Now let us open the box and look how this is implemented. The method printString which re-
turns a string in fact send the message printStringLimitedTo: to its receiver. Finally the method
printStringLimitedTo: defined on the class Object does some calculation and send the message
printOn: to the receiver. The figure 20.12 illustrates the situtation described by the following methods
(see method 20.4).

-2001 Stéphane Ducasse ([email protected])


182 Inheritance

Method 20.4

Object>>printString
"Answer a String whose characters are a description of the receiver.
If you want to print without a character limit, use fullPrintString."

^ self printStringLimitedTo: 50000

Object>>printStringLimitedTo: limit
"Answer a String whose characters are a description of the receiver.
If you want to print without a character limit, use fullPrintString."
| limitedString |
limitedString := String streamContents: [:s | self printOn: s] limitedTo: limit.
limitedString size < limit ifTrue: [^ limitedString].
^ limitedString , ’...etc...’

Object>>printOn: aStream
"Append to the argument, aStream, a sequence of characters that
identifies the receiver."

| title |
title := self class name.
aStream
nextPutAll: (title first isVowel ifTrue: [’an ’] ifFalse: [’a ’]);
nextPutAll: title

What you see is that when the message printString is sent to an instance of EscapingFlasher
then the method is lookup formits class up to Object where it is found. Then the methods is executed, this
means that the method printStringLimitedTo: is looked up starting from the class of the receiver,
the class EscapingFlasher in our case. It is found in Object again. This method finally sent the
message printOn: to self. This method is looked up starting from the class EscapingFlasher
where it is found. So this is the method printOn: defined in this class that is executed and not the one
defined in the class Object.
It is common to say that self is dynamic. This is by reference to the fact that the lookup always starts
in the class of the receiver.

7 Extending Ancestor Behavior

Up until we saw how we can add new behavior to a subclass or redefine a behavior by hidding the exisiting
behavior of a superclass. There are occasions where we would like to be able to extend the behavior of
the superclass, i.e., to execute it but to add some specific actions. This is what we needed for the flasher
morph. For example, we needed to specialize the methods step so that in addition to changing the color
of the morph to make it flashing on a regular basis we wanted to make it moves.
Smalltalk provides a way to invoke the hidden methods (not hidden methods can just be called by
sending a message to self): we send messages to the variable super. In the method 20.5 defined on the
class EscapingFlasher we want to invoke the step methods defined higher in the superclasses so we
send the message step to the variable super.
8. Understanding super 183

Method 20.5

EscapingFlasher>>step
"At each step change the position of the morph by adding a constant distance represen

super step.
self position: self position + (2@2)

Note that the place where you use super does not have to be the first expression of the method body.
Most of the time it is because we first want to execute the hidden methods and then later performing extra
actions. But you may use super everywhere you use self. the variable super is like self it represents
the receiver of the message. super just chages the way methods are lookep up as we show below.

Designer Hints
You should only use super xxx in the body of method whose name is xxx.

Method 20.6

MyClass>>>xxx
...
.... super xxx
...

If you have to use super in a method whose name is different than the one you are invoking
via super there is something wrong with your code.
Designer Hints

Designer Hints
Normally you only need to use super when you are defining a method in a class and in the
same time want to use the method you are hidding by defining this method in your class. If
you are not hidding a method just use self.
Designer Hints

8 Understanding super
The semantics of super may be confusing at first. We want now to explain you how it works by showing
you how sending a message to super change the way the methods are looked up. super like self is a
variable that represents the receiver of the message. super just changes the way the methods are looked
up.

Important!

The lookup of a method for a message send to super starts in the super-
class of the class of the method containing the message.

Illustration. The lookup of a method for a message send to super starts in the superclass of the class
of the method containing the message. The figure 20.13 presents two scenario that should help you to
understand deeply the way super works. The first scenario (noted with a big 1) is the following one: the
message step is sent to an instance of the class EscapingFlasher, the method lookup starts then
in this class. The method step defined in the class EscapingFlasher is found and executed. This

-2001 Stéphane Ducasse ([email protected])


184 Inheritance

Figure 20.13: super is dynamic because it always represents the receiver of the message. This means that
all the message sends sent to self are looked up by starting in the class of the receiver.

method sends a message step to the variable super, so the lookup does not start in the class of the
receiver, EscapingFlasher but in the superclass of the class that contain the first method step, the
class FlasherMorph.
The second scenario (noted with a big A) is here to stress the key point in the change of the lookup.
The message step is sent to an instance of the class EscapingMorphWithKeyboardControl, the
method lookup starts then in this class. The method step defined in the class EscapingFlasher
is found and executed. This method sends a message step to the variable super, so the lookup does
not start in the class of the receiver, EscapingMorphWithKeyboardControl nor in Escaping-
Flasher but in the superclass of the class that contain the first method step, the superclass of Escaping-
Flasher: the class FlasherMorph .
This second scenario illustrates that contrary to self which is dynamic in the sense that the method
lookup starts always from the class of the receiver, super is static in the sense that the method lookup is
not influenced by the receiver. Looking up method for super sends starts always in the superclass of the
class that contain them.

Important!
Looking up methods for super sends starts always in the superclass of the
class that contain them.

Hot Pepper
It is common that people think that super starts by looking up the method in the superclass
of the class of the receiver. It happens that professionals, experts or even book authors think
that super works this way. If the semantics of super would be this one no object-oriented
9. In Summary. 185

language would work because it would loops. Follow the lookup of the method step in the
second scenario of figure 20.13. You should see that the superclass of the class of the receiver
is EscapingFlasher so this means that EscapingFlasher»step would called itself
and lead to an infinite loop, while we want the method step in FlasherMorph to be exe-
cuted when using super step in the method EscapingFlasher»step.
Hot Pepper End
Teacher’s Corner
Pay attention to the vocabulary you use, even if students may not notice at first this will help
you to have a consistent vocabulary: use message send when you talk about a request for the
execution and method execution when such a method has been found.
Teacher’s Corner

9 In Summary.
Important!
When an object receives a message the lookup for a method with the same
selector start in the class of the receiver.

Important!
self send, i.e., messages sent to the variable self are always looked
up by starting from the class of the receiver and continuing in its su-
perclasses.

Important!
Looking up methods for super sends starts always in the superclass of the
class that contain them.

10 Exercises
We strongly suggest you to read again the chapter 19 to really understand what we made and why. The
present chapter explains the key concepts of inheritance and this is extremely important that you understand
it well.

Flasher Variations. Browse the class FlasherMorph and understands how it works. Try to implement
the following variations and have fun discovering how to do it.
◦ The flasher should be flashing in green when it is hit for the first time or when is going up.
◦ Change the intensity of the color according to the speed of the flasher. The more you click it the
brighter. Look into the class FlasherMorph and Color.
◦ Change the size of the flasher according to the number of times it gets clicked or the number of times
it get pointed to by the mouse.
◦ Define the direction to which the morph should go randomly. 10 atRandom returns a number
between 1 and 10.
◦ Implement the accelerating flasher presented in 5 by creating a subclass of the class Escaping-
Flasher presented in section 1.

-2001 Stéphane Ducasse ([email protected])


186 Inheritance

Box and BoxT. Re-implement the class BoxT presented in chapter 22 as a subclass of Box.
The class Box defines the method shrink: that reduces the size of a box. Now we propose you to
implement a variant of shrink: that ensures that the size a box cannot be smaller than a certain size.
Modifying directly the method shrink: in the class Box is shown in method 20.7. Propose a solution
where you define a subclass of the class Box and where you define a method shrink: implementing this
minimal behavior but by using super.
Method 20.7

Box>>shrink: decrement
self undraw.
decrement > size
ifTrue: [size := 5]
ifFalse: [size := size - decrement].
self draw
Concept Summary 21
Programming a Simple
Morph
In this chapter you will apply what you learned in the previous chapter. This is a good repetition before
implementing the Bot environment. For that purpose we propose you to build your own turtle. This way
you will check how to create a simple Morph, and how you can define a class by refining another one and
reusing its properties.
For the sake of simplicity we will not really recreate exactly the same turtle than the one defined in the
class Turtle. However, we will show the important points that have to be addressed when defining a new
class by specializing an existing class. We will show you how to obtain a simple turtle class only with a
couple of methods. While doing this exercise we will show you how to take advantage of the interactivity
of Squeak.
You should be confortable with the practices presented in this chapter because they are basis of object-
oriented programming and they will be used repeatedly during the project Bot the Robot proposed in the
chapter ?? and its subsequent chapters. In this chapter we will first show you how to define a class then we
will analyze all the steps we took to do so and stress the important points that you should understand.

1 The MiniTurtle class


The mini turtle like its big cousin, the normal turtle, is a graphical entity living in the world of Squeak.
To represent such an entity we use the graphical library of Squeak called Morphic. The easiest way to
create a graphical class in Squeak is to define a new class which inherits from the class Morph or one of
its subclasses and defines the desired behavior. For more complex graphical objects, one needs to do the
same and in addition combine several other graphical objects. For the mini turtle we only need the first
approach.

Script 21.1 (A mini turtle in action @@Picture to change@@)


| mcaro |
mcaro := MiniTurtle newStandAlone
openInWorld.
mcaro jump: 100 ; go: 100 ;
turn: -90 ; go: 100

To create graphical objects, they have to be instances of graphical classes,i.e., classes that inherit di-
rectly or indirectly from the class Morph. The class Morph represents the functionality that any graphical

-2001 Stéphane Ducasse ([email protected])


188 Programming a Simple Morph

Figure 21.1: The browser shows you that a new class has been created by displaying it in the second pane
from the left.

object can have.1 . Subclasses of Morph refine such functionality in various ways as the impressive inheri-
tance hierarchy shows it (in a browser select the class Morph and ask an hierarchy browser h).
A mini turtle has the same functionality than a normal one as shown by the script script 21.1.
To create a class, create first a new category (which represents a folder for all the classes we create
related to this small project) by selecting the item addItem of the menu associated with the leftmost pane
of the browser (See Chapter chapter 17). Name it for example MiniTurtle Cat. Select the newly
created category, the browser shows you an empty class template that you should fill. Clearly the class
MiniTurtle should be a subclass of the class Morph now we shoudl think about the instance variables
we need. Note that you can compile using the accept menu item the class and then latter add instance
variables.
To implement the behavior of a mini turtle we need to be able to represent its location on the screen and
its direction. The class Morph already provides the first one via the methods position and position:
because any morph should be able to know its position on the screen.
Fill this template to obtain the definition presented in class 21.1 and select compile it using the menu
item accept. This means that the class MiniTurtle inherits from the class Morph (this is equivalent to
say that the class Morph defines a new subclass as shown by the template. Remark that this second form
is used by the system because the class MiniTurtle does not exist while Morph does, so we can send
the message subclass:... to the class Morph and no message to the class MiniTurtle as it does
not exist yet). Then the class definition states that the class MiniTurtle has one extra instance variable
named direction than its superclass, the class Morph.
Class 21.1

Morph subclass: #MiniTurtle


instanceVariableNames: ’direction’
classVariableNames: ’’
poolDictionaries: ’’
category: ’MiniTurtle Cat’

Note that as soon as you compiler a class using the menu item accept the class exists now. The system
shows you this information by displaying the newly created class in the second pane from the left as shown
in the figure 21.1. This means that we could already create instances of this class, even if now this is not
really useful since they do not have any extra behavior or state than a normal morph.
Now we will define methods one by one and show how as soon as the method is defined it can be used.
Execute the following expression, you should obtain a blue box in the right corner of your screen as shown
by the figure 21.2.
1 The Morphic system is powerful, but is from a software engineering point of view in bad shape. It may happen that at the time
you will read this book, the system already get refactored. In any case we use a limited set of functionality offered by the Morphic
system.
2. Initializing a Mini Turtle 189

Figure 21.2: The class MiniTurtle does not specify how its instances are drawn so the default way
defined by the class Morph is used. We obtain a blue rectangle whose size represents the extent of the
morph.

Figure 21.3: Using an inspector to query the state of object. Here the method height, width, bounds
and extent have been invoked.

Script 21.2

MiniTurtle newStandAlone openInWorld


The class MiniTurtle does not specify yet how its instances are drawn so the default way defined
by the class Morph is used. The surface of the morph is filled by the color blue. This default behavior is
generic and not really interesting even if lot of the morph behavior is already possible such as changing its
color, size....
As shown in figure 21.3, use an inspector (see chapter 16) to get the extent of the box. You should
noticed that the extent is a rectangle. As we would like to draw a circle for rendering the turtle we would
like that the extent be a square. We will fix this in the following.

2 Initializing a Mini Turtle


To change the extent of newly created mini turtle, we can send the message extent: as follow, MiniTurtle
newStandAlone openInWorld extent: 40@40. However, this approach is tedious and more
important, if we pass our code to another person, such a person may not know that he should to that. In fact
this is the responsibility of the mini turtle to initialize its state well. For that purpose we will specialize the
method initializeToStandAlone which is called by the method newStandAlone defined on the
class Morph.

-2001 Stéphane Ducasse ([email protected])


190 Programming a Simple Morph

Method 21.1

MiniTurtle>>initializeToStandAlone

super initializeToStandAlone.
self extent: 50 @ 50.

Define the method initializeToStandAlone in the class MiniTurtle . Now each time a mini
turtle is created by the method newStandAlone the method initializeToStandAlone is invoked
and the newly created mini turtle is are well initialized. Create a new mini turtle and convince you that its
extent has been well initialized.

Completely Initializing MiniTurtle . The direction of the mini turtle is not initialized nor its color. So
redefine the initializeToStandAlone method to take into account the initialization of the direction
and the color of the mini turtle which should be pink when created.
Method 21.2

MiniTurtle>>initializeToStandAlone

super initializeToStandAlone.
self extent: 50 @ 50.
direction := 0.
self color: (Color r: 1.0 g: 0.097 b: 0.774)

It is the responsibility of a class to initialize correctly its instances, not the respon-
sibilities of its users. Use for that the method initialize that is invoked by the
creation method new. For Morphs use the method initializeToStandAlone
that is invoked by the method newStandAlone.

3 Drawing a Mini Turtle


Now we create a graphical representation for the mini turtle. The key point when drawing a morph is that
its graphical representation should not draw outside of the morph extent, else when the morph is moved it
will let dirt on the screen.
Drawing a morph is done by applying graphical operations such as drawing ovals, rectangles or lines
into a canvas. The system performs some complex operations to display a morph and we only have to
specify the drawOn: method whose responsibility is to draw the morph graphical aspect.
The method Rectangle»fillOval:color: that draws an oval of a given size and of a given
color requires as second argument a rectangle that represents the shape of the oval drawn. The script 21.3
shows how we obtain a rectangle (here with size of the same length) of 50 width centered around the point
100@100.

Script 21.3

Rectangle center: 100@100 extent: 50


In the class MiniTurtle , defines the following method, accept it, then click on the blue box. It
should now appear like a pink circle. Note that the circle does not take the complete place of the morph
extent because we want to have some space left for drawing the direction in which the turtle is pointing at.
3. Drawing a Mini Turtle 191

Figure 21.4: Computing a point at a certain distance following a given direction from a starting point

Method 21.3

MiniTurtle>>drawOn: aCanvas

aCanvas
fillOval: (Rectangle center: self center
extent: self height - 10)
color: self color.

What you see is that as soon as the new method has been defined and compiled by the system, it is
possible to use it. As the method we just defined is defined on the class MiniTurtle and that the
instance (displayed previously as a blue box) is from this class, the system now automatically calls this
method instead of the one defined on the class Morph.
Now to indicate the direction to which the turtle is pointing at we draw a line from the center of the
morph towards that direction.
The expression self center + (direction degreeCos @ direction degreeSin
negated * headLength) in the method below computes the point that at the distance headLength
from the center of the turtle following the direction to which the turtle points as shown by the figure 21.4.
Method 21.4

MiniTurtle>>drawOn: aCanvas

| headLength |
headLength := self height // 2 - 2.
aCanvas
fillOval: (Rectangle center: self center
extent: self height -10)
color: self color.
aCanvas
line: self center
to: self center
+ (direction degreeCos @ direction degreeSin negated
* headLength)
width: 1
color: Color black

In this method we see that the result of the method center is used at three different places, so we can

-2001 Stéphane Ducasse ([email protected])


192 Programming a Simple Morph

compute it once and reuse the result as shown in the final version of this method.
Method 21.5

MiniTurtle>>drawOn: aCanvas

| headLength center |
center := self center.
headLength := self height // 2 - 2.
aCanvas
fillOval: (Rectangle center: center extent: self height -10)
color: self color.
aCanvas
line: center
to: center
+ (direction degreeCos @ direction degreeSin negated
* headLength)
width: 1
color: Color black

Now from within an inspector, change the value of the direction instance variable and click on the mini
turtle. The line indicating the direction will point in this direction. In fact you only see the effect when you
will click on morph because at this time the morph is redraw.

4 Some more operations


Now we define some of the operations that a mini turtle can perform. For that we need to define a method
to make turtle draw line on the screen. We give the definition of such a method method 21.6 but we will
not explain this method. Just define it and compile it on the class PasteUpMorph (menu item find class
in the leftmost pane).
Method 21.6

drawLineFrom: oldPoint to: newPoint color: aColor size: anInteger


"Draw a line from a point to a new point with aColor and a size"
| origin offset |
oldPoint = newPoint ifTrue: [^ self].
self createOrResizeTrailsForm.
origin := self topLeft.
turtlePen sourceForm width ~= anInteger
ifTrue: [turtlePen squareNib: anInteger].
offset := anInteger // 2 @ (anInteger // 2).
turtlePen color: aColor.
turtlePen
drawFrom: (oldPoint - origin - offset) asIntegerPoint
to: (newPoint - origin - offset) asIntegerPoint.
self invalidRect: ((oldPoint rect: newPoint) expandBy: anInteger)

Now we are ready to define the behavior of the mini turtles. Let us start with the jumpAt: method
that positions a mini turtle at a given position represented by a point. As we consider that the position of a
turtle is its center, here we just need to change the center of the morph that represents the mini turtle.
4. Some more operations 193

Method 21.7

MiniTurtle>>jumpAt: aPoint
"change the receiver’s position so that its center is placed
at the position aPoint"

self center: aPoint

Now this is easy to define the method goAt: as shown in the method definition method 21.8. It just
draws a line from the current center to the final point and it just there. The expression trailMorph
returns the screen in which the turtle evolves.
Method 21.8

MiniTurtle>>goAt: aPoint
"make the receiver go at a given point of the screen. The receiver
lets a trace on the screen from its current position to the final
point."

self trailMorph
drawLineFrom: self center to: aPoint color: Color black size: 1.
self center: aPoint

The method go: distance moves forward a mini turtle, just have to invokes the method goAt:
aPoint by specifying a point that is in the direction to which the receiver points at and from the given
distance (See method definition method 21.9).
Method 21.9

MiniTurtle>>go: distance

self goAt: self center


+ (direction degreeCos @ direction degreeSin negated
* distance) asIntegerPoint

Similarly the method jump: does the same but by invoking jumpAt: instead of goAt: (see method
definition method 21.10).
Method 21.10

MiniTurtle>>jump: distance

self jumpAt: self center


+ (direction degreeCos @ direction degreeSin negated
* distance) asIntegerPoint

However, there is something not really satisfying in the methods method 21.10 and method 21.9, we de-
fine twice the same complex expression. Worse this expression was already used in the method drawOn:
(See method definition method 21.5). Such an expression is based on the fact that the direction of the
turtle changes according to the mathematical sense (anticlockwise), now if we want to change it and make
it clockwise we will have to fix these three methods. May be while doing that we will forget one of the
methods and then the code will be broken. So the right approach is to create a method called for example
positionInDirectionForDistance: and to invoke from all the places.

-2001 Stéphane Ducasse ([email protected])


194 Programming a Simple Morph

So let’s do it, we start by defining the method positionInDirectionForDistance: (method 21.11)


then we invoke from the other methods (methods method 21.12, method 21.13, and method 21.14. Note in
particular that the resulting methods are shorter and much more readable.
Method 21.11

MiniTurtle>>positionInDirectionForDistance: aDistance
"return the point that is at a distance aDistance in the direction
pointed by the receiver"

^ self center
+ (direction degreeCos @ direction degreeSin negated
* aDistance) asIntegerPoint

Method 21.12

MiniTurtle>>go: distance

self goAt: (self positionInDirectionForDistance: distance)

Method 21.13

MiniTurtle>>jump: distance

self jumpAt: (self positionInDirectionForDistance: distance)

Method 21.14

MiniTurtle>>drawOn: aCanvas

| headLength center |
center := self center.
headLength := self height // 2 - 2.
aCanvas
fillOval: (Rectangle center: center extent: self height -10)
color: self color.
aCanvas
line: center
to: (self positionInDirectionForDistance: headLength)
width: 1
color: Color black

Note that the process is not wrong, we started to define our methods then we realize that some code
could be shared and we refactor it to share the new definition. There is no problem not to see upfront that
the definition of the position computation could have been shared. This is mistake not to refactor the code.

Teacher’s Corner
You may wonder why we did not proposed directly the good solution with the code refactored
but this is on purpose that we made it that way to show the process. It would have been also
really good to show that if we change the convention for the turtle direction we would have to
fix the code in three different places. We did not make it to avoid slowing down the chapter.
5. Fondamental Aspects of Object-Oriented Programming 195

Teacher’s Corner

Finally we have to define the method turn:, turnLeft:, tunrRight: as follows:


Method 21.15

MiniTurtle>>turn: degrees

direction := direction + degrees

MiniTurtle>>turnLeft: degrees

self turn: degrees

MiniTurtle>>turnRight: degrees

self turn: degrees negated

5 Fondamental Aspects of Object-Oriented Programming


Now we would like to stress some key points of object-oriented programming by analyzing the implemen-
tation of the mini turtle.
The method initialize that we shown in the method ?? and that is repeated hereafter in the
method 21.16 illustrates a key point of object-oriented programming. We can invoke methods defined
on the superclass, here Morph, as if they were defined in the class MiniTurtle . Here the methods
extent: and color: are defined in the class Morph but we invoke them using the pseudo-variable
self like any other methods defined in the class MiniTurtle .
Method 21.16

MiniTurtle>>initializeToStandAlone

super initializeToStandAlone.
self extent: 50 @ 50.
direction := 0.
self color: (Color r: 1.0 g: 0.097 b: 0.774)

The case of the method initialize is different because we are re-defining this method, which
is originally defined on the class Morph, on the class MiniTurtle and we want that the method
(Morph»initialize) defined on the class Morph to be still executed. In that case, using self to
invoke the method Morph»initialize would lead to a loop, the method calling itself without any con-
dition to stop this infernal circle. That’s why another pseudo-variable, named super representing the the
receiver of the message exists. super allows one to invoke methods overriden, i.e., method defined in
superclass and redifined in subclasses.
Note that if a method exists in a superclass and a subclass redefines it without using super to invoke
it, this ancestor method is just not called.
As a rule of the thumb, always use self to invoke method except when you are defining a method
whose already exist in the superclass and that you want to invoke. Never use super with a different
method name that the method which contains it.

-2001 Stéphane Ducasse ([email protected])


196 Programming a Simple Morph

Method 21.17

MyClass>>badPractice

... super anotherMethodName

Always use self to invoke methods, even if there are defined in superclass of your
class.

Only use super to invoke methods when you are defining a method that is already
defined in a superclass and that you want this method to be still executed.

Here are the guidelines that you should follow when you use accessor method for your own class or for
classes defined in the system.
Be Consistent. Apply systematically direct access or accessor methods and not to mix both inside a class.
This is extremely confusing for the reader.
Be Private. If you use accessors you should always consider that accessors are kind of private methods
that should not often be called from outside of a class. When you call an accessor from outside of
the class this should raise a bell and you should check if what you are doing is correct.
Respect your parents. Avoid to access directly superclass instance variables. Even if it is possible to
directly access instance variables of a superclass, it is a bad practice. It links outrageously the
implementation of the superclass with the one of the class.

Do not directly access instance variables of superclass.

6 Experimentations
It would be nice to be able to be able to change:
◦ the color of the turtle trace by sending the message inkColor:. Introduce such a functionality, and
◦ the size of the pencil use by sending pencilSize: as shown by the following script.

Script 21.4

| mini |
mini := MiniTurtle newStandAlone openInWorld.
mini inkColor: Color orange ; pencilSize: 3.
Part III

The world of Bot the Robot

-2001 Stéphane Ducasse ([email protected])


Concept Summary 22
The World of Bot The
Robot

In this chapter we describe the application you will build in the subsequent chapter: a robot and its
environment. We will start by building the bot and the elements such as the wall, the floor and the diamonds,
then refine then to introduce more and more functionality.
The robot we want to build lives in a world composed by various tiles such as brick or plain tiles. . . That’s
why we start to define the basic functionalities of a tile and a simple world.
One particular problem we will address is how to represent the location of the tiles in the world. Doing
so you will learn how a given structure, here linear array, can be used to represent another one, here a two
dimensional array. This is a general technique that is commonly used and is worth mastering.

-2001 Stéphane Ducasse ([email protected])


200 The World of Bot The Robot

Figure 22.1:

Figure 22.2:

Figure 22.3:
1. Basic Tile 201

Figure 22.4: A plain tile as ground for the bot world.

the robot can move one tile at a time and can only move from one tile another one.

1 Basic Tile
A tile is a graphical object which has a border so we decided to define it has a subclass of the class
BorderedMorph as shown by class ??.
Class 22.1

BorderedMorph subclass: #BotWorldTile


instanceVariableNames: ’’
classVariableNames: ’’
poolDictionaries: ’’
category: ’Morphic-Games’

Once the class is defined, we need to specify how a basic tile should look like. We do this by defining a
new method named initialize which is called by the new method when an newly created tile instance
is created. Once this method defined try to create a tile as shown by script 22.1 and by the figure ??. You
can use an inspector as explained in chapter ?? or the menu halo to modify some values of the tile.

Script 22.1

BotWorldTile new openInWorld

Method 22.1

BotWorldTile>>initialize
"BotWorldTile new openInWorld"

super initialize.
self color: Color blue lighter.
self borderColor: Color blue muchLighter.
self bounds: (0@0 corner: 32@32).
self useSquareCorners.
self borderStyle: (BorderStyle complexAltFramed width: 2).

2 Design Discussions
Up until now it was not really useful to create a new class to represent a tile. Indeed, we did not define any
new behavior so creating an instance as shown in script ?? is equivalent to script 22.2. Creating a class is
necessary when new behavior or specialization of existing behavior is defined.

-2001 Stéphane Ducasse ([email protected])


202 The World of Bot The Robot

Script 22.2 (Tile equivalent creation)

| borderedMorph |
borderedMorph := BorderedMorph new.
borderedMorph color: Color blue lighter.
borderedMorph borderColor: Color blue muchLighter.
borderedMorph bounds: (0@0 corner: 32@32).
borderedMorph useSquareCorners.
borderedMorph borderStyle: (BorderStyle complexAltFramed width: 2).
This solution makes sense if we use an instance of BorderedMorph and only want to change some
of its default setup. We could also have created a new method in the class BorderedMorph named for
example botTile and invoke it on an instance of BorderedMorph as show by script 22.3.
Method 22.2

Bordered>>botTile

self color: Color blue lighter.


self borderColor: Color blue muchLighter.
self bounds: (0@0 corner: 32@32).
self useSquareCorners.
self borderStyle: (BorderStyle complexAltFramed width: 2).

Script 22.3 (Tile equivalent creation)

| borderedMorph |
borderedMorph := BorderedMorph new.
borderedMorph botTile.
This last approach is only interesting if the new method created makes sense for the class BorderedMorph.
Applying too often it leads to classes having too much methods that are not of real interest for the class
itself. Moreover, the class BorderedMorph is a class of the system and not a class that you created, and
this is often a bad practice to define methods on such system classes.
A good heuristic to determine if it is worth to define a method on a system class is to evaluate if
sombody else than you could need it.

3 Bot’s World
As the world will be later on embedded in other widgets, we define it as an instance of the class AlignmentMorph.
The world should represents the area that will contain the tiles, the robot and the diamond. For such a rea-
son, we define an instance variable named matrix which represents the internal representation of the world
(see ??).
Class 22.2

AlignmentMorph subclass: #BotWorld


instanceVariableNames: ’areaMatrix ’
classVariableNames: ’’
poolDictionaries: ’’
category: ’Morphic-Games’
4. First Improvements 203

4 First Improvements
What about having a bigger world. Try to get a world of 20 rows on 18 columns. As you are realizing,
having hardcoded number such as the size of the matrix, its number of columns, and rows is not really good
because if we want to change them, we will have to find all the places where we used them and change
them. It is better to define methods that return their values and use them. This is another illustration of the
rule, Never say twice”, that says that we should avoid to repeat an information.
Define the methods rowNumber, columnNumber, and numberOfTiles and use them instead of
the hardcoded values.
Method 22.3

BotWorld>>rowNumber

^ 12

Method 22.4

BotWorld>>columnNumber

^ 15

Method 22.5

BotWorld>>numberOfTiles

^ self columnNumber * self rowNumber

Now you factored out the knowledge relative to the size of the world. In a future step we could avoid
to have a recompile the methods rowNumber and columnNumber. Imagine how this could be accom-
plished (Hint: we would certainly need some extra instance variables).

5 Final improvement
As we would like to have the possibility to change the size of the all the tiles and we only expect to use the
@@Not sure that I want to have this couipling with BotWorld@@
Method 22.6

BotWorldTile>>initialize
"BotWorldTile new openInWorld"

super initialize.
self color: Color blue lighter.
self borderColor: Color blue muchLighter.
self bounds: BotWorld defaultTileSize.
self useSquareCorners.
self borderStyle: (BorderStyle complexAltFramed width: 2).

-2001 Stéphane Ducasse ([email protected])


204 The World of Bot The Robot

Method 22.7

BotWorld class>>defaultTileSize

^ (0@0 corner: 32@32)


Part IV

Projects – L-Systems: Fractals and


Plant Growth Modeling

-2001 Stéphane Ducasse ([email protected])


207

Lindermayer systems, also called L-Systems, have been developed by biology theorists to understand
and simulate the growth of plants [?]. In the following chapters we propose you to implement different
L-Systems from a minimalist to elaborate ones. Chapter 23 presents the simplest possible L-Systems, i.e.,
L-System composed by one single rule. Chapter 24 presents an object-oriented model of L-System that
alleviate the limits of the previous implementation. Chapter 25 defines a new class of turtle that allows
one to model plants with the L-Systems defined previously. Finally, Chapter 26 introduces Parametric
L-Systems that allows one to express more powerful L-Systems. This chapter shows in particular how
inheritance can be used to reuse class already defined.

-2001 Stéphane Ducasse ([email protected])


208
Project 23
Simple L-Systems and
Fractal Production

For Reviewers: This is the first project we want to propose. We do not know
yet if the book will contain recursion explanation. If this would be the case we
would like to add some links from this chapter to the one presenting recursion.
Aristide Lindermayer worked on the understanding and representation of plant growth. For that pur-
pose, he invented rewriting rule-based systems, called L-Systems [?] and was able to accuretely represent
the growth of some basic plants and algua. In this chapter, we propose you to implement the simplest form
of L-System: a system with a single rule. In the chapter 24, we will extend the approach proposed to be
able to have multiple rules and really represent plant growth. Although L-Systems have been elaborated
to describe the plant growth, they can also be applied to produce fractal graphics (graphics based on rep-
etitions of themselves). Moreover, the graphical interpretation of L-Systems is easy with a turtle. This is
what we explore in this chapter.

1 L-Systems
L-Systems is a generic term of designing different rule based rewriting systems. A rewriting rule based
system is a system that given an input like a sequence of characters, strings or numbers and the set of rules
explaining how an element of the input is replaced by new ones, produces an output.
Let us look at the simple example1 below.
1A fun aspect of this L-System is that the length of the production is the Fibonacci suite.

-2001 Stéphane Ducasse ([email protected])


210 Simple L-Systems and Fractal Production

Fibonacci
Rule 1: b → a
Rule 2: a → a b

It is composed by two rules, the first meaning that every b has to be replaced by an a, the second
meaning that every a has to the replaced by ab.
Now if we start with b and apply in parallel the two rules we obtain the following results where each
line represents the application of the rules to the result of the previous line.

Axiom b
(Rule 1) →a
(Rule 2) →ab
(Rules 2, 1) →aba
(Rules 2, 1, 2) →abaab
(Rules 2, 1, 2, 2, 1) →abaababa

Vocabulary point. We call the input, in the example b, the axiom of the L-System. We say that we apply
a rule. Applying several times a rule is also named deriving the axiom. The result of the rule application
the production or derivation of the L-System after a certain number of iteration. We call vocabulary the
symbols a and b that constitutes the axiom and its production.

Analysis. Let us analyse this simple L-System2 and understand the basic mechanisms used here:

◦ An L-System can contains several rules. Here we have two rules namely Rule 1 that transforms every
element b into a and Rule 2 that transforms every a into the sequence of elements ab.
◦ A rule is composed by two parts: a left and a right part. The left part identifies the element in the
input that will be replaced by the right part. What is important to see is that the left part of a rule only
identifies one element while the right part can be one or multiple elements.
◦ Applying a rule means replacing in the current input the element (left part of the rule) by the sequence
of elements described in the right part of the rule. For example, the third line above is produced by
applying the Rule 1 and substituing a by ab.
◦ The rules are applied in parallel, each time they can be applied. For example, the production of
the fourth line is the result of applying the Rule 2 and the Rule 1 to the sequence ab. Note that the
application order is irrevelant as long as it is performed in parallel, we just mentionned it here because
we read from left to right.
◦ A rule can be applied several times. The production of the last line is the result of the application of
the rules 2, 1, 2, 2 and 1.
◦ The axiom does not have to be necessary one single element.
◦ In the type of L-System presented here, and called context-free L-Systems, the application of a rule
does not have to take care about the context (the elements before and after the current element being
changed) in which it occurs.

Parallel or Sequential Rule Application. In computer science, language grammars are often described
using rewiting rule based like systems. However, the production of an L-System is the result of the parallel
application of the rules, whereas in language grammar rules are applied in sequence, only one rule at during
one iteration. The reason is that both systems are not used to model similar concept. L-Systems parallel
rule application is adapted to represent the parallel growth of plant cellula, while grammars are used to
represent choice or paths to follow.
2 Certain L-Systems are much more complex and take into account the context of the element or some other parameters before

applying the rule.


2. Graphical Interpretation of L-Systems 211

Figure 23.1: Steps 0, 1, 2 and 3 of the L-System defined by Axiom: F, Angle=90, F → F+F-F-FF+F+F-F.
As a segment is cut into 4 segments, we divide the length by 4 from iteration to iteration. The lengths of
the picture are 64 * 4, 64, 16 and 4.

2 Graphical Interpretation of L-Systems


Besides representing cellula growth, L-Systems are also interesting systems because we can use them to
produce graphics representing plants or fractals. Fractals are graphics that are built using repetitive patterns
and that also partially or fully contain themselves. The idea to produce graphics based on L-Systems is to
use L-System whose vocabulary that can be interpreted graphically. Let us see that!

2.1 Turtle Dialect for L-Systems


Researchers have been using turtle like technology to interpret graphically L-Systems and produce graph-
ics. To that purpose, they constraint L-System vocabulary the following way: Axiom and rules only ma-
nipulate a limited set of predefined symbols representing turtle methods. These symbols are:
F : the turtle goes forward and leaves a trace from a given distance. It corresponds to our go: method.
f : the turtle goes forward without leaving a trace from a given distance. It corresponds to our jump:
method.
+ : the turtle turns on the left from a given angle. It corresponds to our turnLeft: method.
- : the turtle turns on the right from a given angle. It corresponds to our turnRight: method.
This simplest form of L-System are based on an angle and a length to move forward that are defined
once for all the rules of the L-System. The value of these data does not change during the rules application
or graphical interpretation. This is the reason why you have to pay attention of the length we define if
we want to have reasonable output. In chapter 26 shows a new kind of L-Systems that can manipulate
parameters such the length from which the turtle should move forward.

2.2 First L-System for Turtle


Let us look at the following example whose first application steps are illustrated by the Figure 23.1.

-2001 Stéphane Ducasse ([email protected])


212 Simple L-Systems and Fractal Production

Figure 23.2: The superposition of the graphics produced after the second iteration and after the third
iteration shows how the previous figure is decomposed.

Figure 23.1
Axiom F
Angle 90 degree
Rule F → F+F-F-FF+F+F-F

◦ The axiom represents the geometrical shape that we take as starting point. Here F means that we start
with a line, F+F+F+F a square as the angle is 90 degree.
◦ The rule here described how one segment is transformed in the second steps of the Figure 23.1.

Applying several times the rule produces the other graphics shown in the Figure 23.1. The Figure 23.2
superposes two graphics on the same picture to show how the rule application decomposes the segments
into smaller ones.
The production at the different levels can become really quickly huge. For example here are the pro-
ductions of this L-System for the level application steps : 0 → F, 1 → F+F-F-FF+F+F-F, 2 → F+F-F-
FF+F+F-F+F+F-F-FF+F+F-F-F+F-F-FF+F+F-F-F+F-F-FF+F+F-FF+F-F-FF+F+F-F+F+F-F-FF+F+F-
F+F+F-F-FF+F+F-F-F+F-F-FF+F+F-F, 3 → F+F-F-FF+F+F-F+F+F-F-FF+F+F-F-F+F-F-FF+F+F-
F-F+F-F-FF+F+F-FF+F-F-FF+F+F-F+F+F-F -FF+F+F-F+F+F-F-FF+F+F-F-F+F-F-FF+F+F-F+F+F-
F-FF+F+F-F+F+F-F-FF+F+F-F-F+F-F-FF+F+F-F-F+F-F-FF+F+F-FF+F-F-FF+F+F-F+F+F-F-
FF+F+F-F+F+F-F-FF+F+F-F-F+F-F-FF+F+F-F-F+F-F-FF+F+F-F+F+F-F-FF+F+F-F-F+F-F-FF+F+F-
F-F+F-F-FF+F+F-FF+F-F-FF+F+F-F+F+F-F-FF+F+F-F+F+F-F-FF+F+F-F-F+F-F-FF+F+F-F-
F+F-F-FF+F+F-F+F+F-F-FF+F+F-F-F+F-F-FF+F+F-F-F+F-F-FF+F+F-FF+F-F-FF+F+F-F+F+F-
F-FF+F+F-F+F+F-F-FF+F+F-F-F+F-F-FF+F+F-FF+F-F-FF+F+F-F+F+F-F-FF+F+F-F-F+F-F-
FF+F+F-F-F+F-F-FF+F+F-FF+F-F-FF+F+F-F+F+F-F-FF+F+F-F+F+F-F-FF+F+F-F-F+F-F-FF+F+F-
F+F+F-F-FF+F+F-F+F+F-F-FF+F+F-F-F+F-F-FF+F+F-F-F+F-F-FF+F+F-FF+F-F-FF+F+F-F+F+F-
F-FF+F+F-F+F+F-F-FF+F+F-F-F+F-F-FF+F+F-F+F+F-F-FF+F+F-F+F+F-F-FF+F+F-F-F+F-F-
FF+F+F-F-F+F-F-FF+F+F-FF+F-F-FF+F+F-F+F+F-F-FF+F+F-F+F+F-F-FF+F+F-F-F+F-F-FF+F+F-
F-F+F-F-FF+F+F-F+F+F-F-FF+F+F-F-F+F-F-FF+F+F-F-F+F-F-FF+F+F-FF+F-F-FF+F+F-F+F+F-
F-FF+F+F-F+F+F-F-FF+F+F-F-F+F-F-FF+F+F-F
So take care during your experimentation not to ask crazy numbers, you could think that the system is
down but it would be just computing for hours.

Note. We could have used directly the name of the turtle methods but this would have led to overly long
L-System definition when the L-System is already long as with the one defined in Section ??. Moreover,
we prefer to stick with the original L-System notation.
3. Enhancing the Turtle class 213

3 Enhancing the Turtle class


As shown in the Chapter ?? it is easy to add functionality to the class Turtle so that turtles are able to
interpret a string composed by the L-System symbols (F, f, +, -) and perform the corresponding actions.
You should remember that a string is a collection of characters and that characters are single letter beginning
with $.

Do it. Simple L-Systems defines once the length from which a turtle should go forward and the angle
they should turn. So define the method interpretSymbol: aChar length: len angle:
degree that sends the action described by aChar to the receiver turtle. If the character is $F or $f the
turtle should go forward (with trace and without trace) from len distance. If the character is $+ or $- the
turtle receiver should respectively to the left or right from degree.
The Script 23.1 should draw an U.

Script 23.1

|aTurtle|
aTurtle := Turtle new.
aTurtle interpretSymbol: $F length: 100 angle: 90.
aTurtle interpretSymbol: $+ length: 100 angle: 90.
aTurtle interpretSymbol: $F length: 100 angle: 90.
aTurtle interpretSymbol: $+ length: 100 angle: 90.
aTurtle interpretSymbol: $f length: 100 angle: 90.
aTurtle interpretSymbol: $+ length: 100 angle: 90.
aTurtle interpretSymbol: $F length: 100 angle: 90.

Do it. Define the method interpret: aCollection length: len angle: degree that
knows how to interpret a string, i.e., a collection of characters. Hints: String is a subclass of Collection so
operations possible on collections are also possible on strings.
The Script 23.2 should also draw an U.

Script 23.2

|aTurtle|
aTurtle := Turtle new.
aTurtle interpret: ’F+F+f+F’ length: 100 angle: 90.

4 A Simple Approach
For now we limit our approach to the simplest L-System: a system containing only one rule. In the next
chapters we will improve the solution to handle more complex L-System. We took this decision so that
you could have a fast idea of the process.
What is missing now is the application of the rule on an input. It would be also interesting to have the
possibility to specify an axiom, a rule and the number of time that the rule is applied. Define the method
deriveAxiom: aString withRule: leftPart give: rightPart atLevel: n. Just
remember that deriving the axiom a certain number of time requires to apply any possible rules then reiter-
ate the rule application on the result you obtained previously.
To help you, look at the method copyReplaceAll:with: defined on the class SequenceCollection.

-2001 Stéphane Ducasse ([email protected])


214 Simple L-Systems and Fractal Production

Script 23.3

|aTurtle|
aTurtle := Turtle new.
aTurtle deriveAxiom: ’F’ withRule: ’F’ give: ’F+F-F-FF+F+F-F’ atLevel: 1
returns ’F+F-F-FF+F+F-F’
aTurtle deriveAxiom: ’F’ withRule: ’F’ give: ’F+F-F-FF+F+F-F’ atLevel: 0
returns ’F’
aTurtle deriveAxiom: ’F’ withRule: ’F’ give: ’F+F-F-FF+F+F-F’ atLevel: 2
returns ’F+F-F-FF+F+F-F+F+F-F-FF+F+F-F-F+F-F-FF+F+F-F-F+F-F-FF+F+F-FF+F-F-FF+F+F-F+F+

You are there! Now you are in the position to produce your first graphics. You have to use the methods
deriveAxiom:withRule:give:atLevel: and interpret:length:angle:. Try to pro-
gram the L-System we show previously.

Figure 23.3: Variation on Koch curve 2: n = 4, angle = 90, Axiom: F-F-F-F, F → FF-F-F-F-FF

5 Testing Some 1-rule based L-Systems


To help to appreciate the beauty of L-Systems, we show you some of the pictures that you can now create.
The elements of the first picture of this chapter are generated by the following L-System. This family
of curves are called Koch curves.

Figure@@
Axiom F-F-F-F
Angle 90 degree
Rule F → F-F+F+FF-F-F+F

The following L-Systems produce also some beautiful graphics.

Figure@@
Axiom F
Angle 90 degree
Rule F → F+F-F-F+F

Figure@@
Axiom F
5. Testing Some 1-rule based L-Systems 215

Figure 23.4: Variation on Koch curve 1: n = 4, angle = 90, Axiom: F-F-F-F, F → FF-F-F-F-F-F+F

Angle 90 degree
Rule F → F+F-F-F+F

Figure@@
Axiom F-F-F-F
Angle 90 degree
Rule F → F+FF-FF-F-F+F+FF-F-F+F+FF+FF-F

Figure 23.5: Variation on Koch curve 3: n = 3, angle = 90, Axiom: F-F-F-F, F → FF-F-F+F-F-FF

-2001 Stéphane Ducasse ([email protected])


216 Simple L-Systems and Fractal Production

Figure 23.6: Variation on Koch curve 4: n = 4, angle = 90, Axiom: F-F-F-F, F → FF-F–F-F

Figure 23.7: Variation on Koch curve 5: n = 5, angle = 90, Axiom: F-F-F-F, F → F-FF–F-F
6. Analysis of the Solution 217

Figure 23.8: Variation on Koch curve 6: n = 4, angle = 90, Axiom: F-F-F-F, F → F-F+F-F-F

Experiment 23.1
Experiment and find the L-System generating the previous figure. Hints: start with the smallest interesting
axiom, the smallest level of derivation and play with the production rule to generate the transformation of
a side.

6 Analysis of the Solution


Even if the solution we proposed here is successful to manage trivial L-Systems, its design is quite bad.
Indeed, we mentionned in Chapter 15 that methods should represent the behavior or responsibilities that an
object is carrying. Here interpret:length:angle: and interpretSign:length:angle:
are methods that clearly represent the behavior of a turtle. These methods allow one to present messages
sent in another form, here strings. But this is absolutely not the case for the methods replace:by:in:
and deriveAxiom:withRule:production:atLevel: that have nothing to do with a turtle. First,
the behavior that these methods implement has nothing to do with turtle behavior, second these methods
do not even use the state of the turtle or its methods. This is a sign that they are defined in the wrong place.
So a class representing an L-System have to be created and these methods should be moved there. This is
what we will solved in the following chapter.
The fact that the solution only allows the expression of single rule L-System is not really a problem
because we learnt this way the essence of the problem and are now ready to develop a more complex
system. From a methodological point of view this is a good point. Some people think that producing
the best solution handling all kinds of variations is better, because sometimes we cannot forsee where the
complex part will be. So having some small implementation up front working even in a limited setting is a

-2001 Stéphane Ducasse ([email protected])


218 Simple L-Systems and Fractal Production

way to understand the problem. We should only be prepared to change it heavily or to simply throw away
what we made.
Project 24
Advanced L-Systems as
Objects

In the previous chapter we proposed you to program the simplest form of L-Systems. We also dis-
cussed the proposed solution and explained why this solution was not elegant. In this chapter we propose
you to implement more complex L-Systems that can contain multiple rules and subfigures. The resulting
implementation is reused in the next chapter in which we extend it to simulate plant growth.

1 Modeling L-Systems with Objects


The first implementation of an L-System was extremely simple and consisted of small number of methods.
We already explained that the major drawback of this implementation, besides its intrinsic limitation, is
that some methods that we defined in the class Turtle have nothing to do with a turtle. This is a clear
sign that our design of L-System was not well designed.
As we already mentioned in chapter ref, one of the major difficulties while programming with an object
language is to identify the right objects and the way they collaborate. A good heuristic to follow is that
objects should have a limited set of clearly defined responsibilities and collaborators with which they
interact.

-2001 Stéphane Ducasse ([email protected])


220 Advanced L-Systems as Objects

There is no unique way to implement a given problem, that’s why practionners and researchers devel-
oped methodologies to help identifying objects and developing applications. The one we prefer is respon-
sibilities driven design [?] and is based on the use of CRC (Class Responsibility Collaborators) cards [?].
The idea is to identify for each class, its responsibilities and collaborators and be able to write all these
information on small cards. If a single card is not enough then this is the sign that your class is doing too
much. Then we also like to take one problem at a time without thinking too much in advance for all the
possible extensions that the system may have to take into account. Some other methodologies try to cover
all the possible extensions of an application in advances which may work for well-defined domain and non
changing mind customers. In this book we chose an incremental approach where we implement the objects
when they are needed. Note that finding the right methodology to develop object-oriented systems is and
will continue to stay a hot debate between methodologists and practionners themselves.

1.1 Three classes


In the case of L-Systems we identify three kinds of objects: the LSystem class, the Rule class and the
Turtle class. The Figure 24.1 describes the relationships between these classes.

The class LSystem and its responsibilities. It represents an L-System, i.e., an axiom and a set of rules.
An L-System knows how to derive itself from an axiom and a set of production rules. An L-System
is composed by rules and collaborates with a turtle that interprets graphically its production.

The class Rule and its responsibilities. It represents a rule that composes an L-System. A rule is com-
posed by a left part and a right part. A rule knows if it matches a given information. Additional
information such conditions that have to hold to apply the rule may also be represented by this class
or subclasses.

The class Turtle and its responsibilities. It represents a sowftare turtle. It can be enhanced to know
how to make drawing from different information.

In fact the class Rule is not absoluletely necessary. There are ways (like using a dictionary or hash
table whose keys represent the left parts and values the right part of rules) to implement the simple rules
directly into the class LSystem. However, we decided to define an explicit class Rule because it is
an important entity of the LSystem domain. Moreover, we wanted to implement specific methods like
printing to help you debugging and as you will see in chapter 26 parametric L-Systems requires more
complex rules.

An Example. The following script shows the creation of an L-System representing the fibonacci suite
shown in 1 and how its derivation can be invoked. The lengths of each production iterations constitutes the
Fibonacci suite (1, 1, 2, 3, 5, 8...).
Script 24.1 (LSystem representing Fibonacci Suite)

LSystem class>>fibonacci
"self fibonacci"

| lsys |
lsys := LSystem new axiom: ’B’.
lsys add: (LSRule leftPart: ’B’ rightPart: ’A’).
lsys add: (LSRule leftPart: ’A’ rightPart: ’AB’).
^lsys deriveAxiomAtLevel: 4
After declaring a variable, a new LSystem instance is created, the axiom ’B’, a string, is specified.
Then two rules are created and added to the L-System. Finally, the L-System is asked to produce and return
the production for the fourth iteration. A rule is composed by a string representing the left part and a string
representing the right part (See 4.1).
2. The Rule class 221

Figure 24.1: Relationships between the different classes participating in the L-System application. (For the
Reviewers: we will explain somewhere the meaning of the signs.

Preparing. Before defining the corresponding classes, open a new project named L-System, this will
allow you to save all the changes you will do in one file and to browse the changes you will perform. Then
open a browser and define a new category named L-System in which we will define all the classes relative
to L-System.

2 The Rule class


The behavior of a rule should allow us to specify the left and right parts of a rule, to test if a given element
of a derivation match the rule and to print a rule so that we can understand them more easily.

Do it. Define the class LSRule that inherits from the class Object. We suffix the name of the class
by LS to avoid name problems1 , it may happen that somebody else already used such a name for another
class. To represent the left and right part of a rule you can choose to use two named instance variables
named for example leftPart and rightPart. Define the accessors for these two instances variables.

2.1 Interface for Instance Creation


As there is no real sense to create a rule without specifying the left part and the right part, define the
class method in the ’instance creation’ protocol named leftPart:rightPart: that creates a rule and
initializes it with the arguments passed as shown by the following script.
Script 24.2

LSRule leftPart: ’A’ rightPart: ’AB’

Deeper: Discussions about Creation Responsibility. Note that by proposing an interface for the cre-
ation of instance you, as responsible of the class, control that the created instance is well-formed. Different
degrees of control can be applied. For example, if you do not want to allow the creation of instance via the
class method new you can override it to raise an error this way:
1 Namespaces have been introduced in other Smalltalks and it may happen that namespaces will be introduced in Squeak too.

-2001 Stéphane Ducasse ([email protected])


222 Advanced L-Systems as Objects

Method 24.1

LSRule class>>new

self error: ’To create an instance of ’,


self name,
’ you should use leftPart:rightPart’.

However, you may notice that this choice implies that you cannot invoke anymore the method new in
the class method leftPart:rightPart: via self. The best way to solve this problem is to invoke
the method basicNew to create a non-initialize instance as shown by the following method. Note that
method starting with basic should never be overriden to be sure that we can always invoke them.
Method 24.2

LSRule class>>leftPart: left rightPart: right

^ self basicNew leftPart: left ; rightPart: right

Another non elegant way to solve this problem would have been to invoke new via the super instance
variable. We strongly discourage you to do so because you would unnecessary coupled classes. This is a
good guidelines to only use super to invoke a method with the same selector that the one containing the
super.

2.2 Rule Behavior


Having well formed rules was the first step. The second step is to be able to know if a given rule can be
applied, i.e., if we can substitute the left part by its right part while doing the axiom derivation. This is
definitively a responsibility of a rule, so implement the method doesMatch: anElement that returns
true if a rule matches a given element.

Script 24.3

| r1 |
r1 := LSRule leftPart: ’A’ rightPart: ’AB’.
r1 doesMatch: ’B’.
returns false
r1 doesMatch: ’A’
returns true
The implementation of doesMatch: is really simple. This simplicity is not a problem! What is
important to realize is that this rule behavior could be much more complex and that providing a method for
it allow us to change it without disturbing the other classes using rules.

Adapted Printing. In Smalltalk, any object can return a string representing it. This is really useful
while developing an application. This behavior is specified by the methods printOn:. Look for all the
implementors of printOn: to see how the system uses this functionality.
By default the system proposes a generic mechanism to print any object, read the code of the method
printOn: defined on the class Object. However, using such a generic printing mechanism does not
provide a lot of information.
For example printing (via the Print It menu) the following expression which could contain more infor-
mation.
2. The Rule class 223

Script 24.4

LSRule leftPart: ’A’ rightPart: ’AB’


displays the string a LSRule

Redefine the method printOn: in the category ’printing’ as follow:


Method 24.3

LSRule>>printOn: aStream

aStream nextPutAll: ’Rule: ’.


aStream nextPutAll: leftPart.
aStream nextPutAll: ’ -> ’.
aStream nextPutAll: rightPart

Once this method is defined, printing the previous rule displays the following text: Rule: B ->
AB which conveys much more information.

Script 24.5

LSRule leftPart: ’A’ rightPart: ’AB’


displays the string Rule B -> AB

Figure 24.2: n = 2, angle = 90, Axiom: F-F-F-F, F → F+FF-FF-F-F+FF-F-F+F+FF+FF-F

-2001 Stéphane Ducasse ([email protected])


224 Advanced L-Systems as Objects

3 The L-System class


An L-System object contains the following information: its axiom and a set of production rules. From
a behavioral perspective, an L-System should provides fonctionality to define such information but more
importantly how to derive the axiom using the rules.

3.1 Class definition and Instance Initialization.


Define the class LSystem that inherits from Object in the ’L-System’ class category. This class
should have two instance variables named for example, axiom and rules to represent the axiom and the
set of rules. The definition of such a class is shown below:
Class 24.1

Object subclass: #LSystem


instanceVariableNames: ’rules axiom’
classVariableNames: ’’
poolDictionaries: ’’
category: ’L-System’

Create a category ’accessing’ and define the accessors relative to the axiom instance variable.

Instance Variable Initialization. As explained in 2.4, specialize the class method new to invoke auto-
matically the method initialize on any newly created instances and to return it.
To represent a set of rule we can different data structures like a set (only one element and no order) , a
bag (multiple occurences of the same element and no order) an array (indexed by integers and not growing),
an ordered collection (ordered but growing), dictionary (indexed by a hash function and growing).
As the order of the rules is irrelevant, we chose to use a set to hold the collection of rules. Define the
method initialize in the category ’initialize’ so that the default value of the axiom instance
variable is an empty string (”) and the value of the rules instance variable is a Set instance.
In the category ’rule addition’, define the method add: that add a given rule to the set of rules
used by the L-System. Hints: Look for information in the class Collection and its subclasses.

3.2 Deeper: Discussions about Set and Identity


A set does not allow to contain twice the same object. This leads to asking the question of object identity.
By default, a set uses the hash value of the object to check if the object can be added to itself. As we did not
redefine the hash value of rule, two instances representing the same rule are in fact two different objects,
hence they are added to the set. In our current implementation this is absolutely not a problem. If you want
to try, define the following method and inspect the result of the method comment.
Method 24.4

LSystem class>>twiceSameRuleAdded
"self twiceSameRuleAdded"

| lsys |
lsys := self new.
lsys add: (LSRule leftPart: ’A’ rightPart: ’AB’).
lsys add: (LSRule leftPart: ’A’ rightPart: ’AB’).
^ lsys

If we would need to avoid such a kind of situation, we should do the following: (1) redefine the =
method of the objects to represent the equality function for the object, and (2) redefine the hash method
3. The L-System class 225

to return a different hash number for the object. As both functions are somehow related we apply the
following pattern suggested by Beck in [?] in the case of a rule.

For Reviewers: Is there any better way to write that?

Method 24.5

LSRule>>= aRule
(aRule isKindOf: self class) ifFalse: [^false].
^ (self leftPart = aRule leftPart) and: [self rightPart = aRule rightPart]

Method 24.6

LSRule>>hash
^ self hash leftPart hash bitXor: self rightPart hash

The method = is redefined: it checks if the class of the argument is the same as or a subclass of the
class of the receiver if this is true, the left and right part should be equals too. The hash method is then
redefine to combine the hash values of the left and right parts. Check by inspecting the value returned by
LSystem twiceSameRuleAdded.

3.3 Axiom Derivation


Deriving the axiom a given number of times is the core behavior of an L-System. We propose you to im-
plement it as three methods: deriveAxiomAtLevel:, treatSequence:, and treatElement:.
deriveAxiomAtLevel: applies a certain number of times the rules. It does it by invoking several
times the method treatSequence: on its own result. treatSequence: applies the rules on each
of the elements contained in the collection passed by calling the treatElement: and returns it result.
treatElement: checks whether a rule can be applied for a given element, if so it returns the right part
of the rule, else the right part.
All the examples illustrating these three methods are shown in the context of the L-System defined
in Script 24.1. So define it as a class method of the class LSystem in category ’examples’ as shown
in Method 24.7. Note that this way you will be able to save this specific LSystem together with the code
that implements the LSystem class.
Method 24.7

LSystem class>>fibonacci
"self fibonacci"

| lsys |
lsys := LSystem new axiom: ’B’.
lsys add: (LSRule leftPart: ’B’ rightPart: ’A’).
lsys add: (LSRule leftPart: ’A’ rightPart: ’AB’).
^lsys deriveAxiomAtLevel: 4

treatElement: anElement checks whether one of the rules matches the passed element. If this is the
case it returns the left part of the rule else it returns the element converted as a string. The fact that the
method always returns the same kind of objects, here a string, is really important, this way the calling
method can use the result in an uniform manner.

-2001 Stéphane Ducasse ([email protected])


226 Advanced L-Systems as Objects

Script 24.6

| fib |
fib := LSystem fibonacci.
fib treatElement: $B.
returns ’A’
fib treatElement: $A.
returns ’AB’
fib treatElement: $C.
returns ’C’
The method treatElement: is called by the method treatSequence: over each element of the
axiom (and the next result of the rule application) which are strings. As a string is a collection of characters,
the element passed to treatElement: is a character.

Hints. Look in the Collection class for the method detect: aBlock ifNone: exceptionBlock
whose behavior is explained by the following script.
Script 24.7

| col |
col := #(a 1 b 2 true).
col detect: [:each | each isNumber] ifNone: [nil].
returns 1
col detect: [:each | each isPoint] ifNone: [nil].
returns nil

treatSequence: aString invokes the method treatElement: on each of the characters con-
tained in the argument, aString and returns a string in which all the results have been concatenated. The
following script shows some results obtained when invoking this method.
Script 24.8

| fib |
fib := LSystem fibonacci.
fib treatSequence: ’A’.
returns ’AB’
fib treatSequence: ’C’.
returns ’C’
fib treatSequence: ’ABC’
returns ’ABAC’
fib treatSequence: ’ABCA’
returns ’ABAABABA’

Hints. Use the method finder to identify the method that given two strings returns the string representing
the concatenation of them. Then one way could be to define a local variable representing the result of
the method, then to initialize with an empty string and to concatenate the results returned by the method
treatElement: to this variable.

deriveAxiomAtLevel: aNumber derives the axiom by applying the rules a certain number of
times. As we shown in the previous chapter in section 1, the derivation at a level n is the result of the
application of the rules on the axiom derivation at level n-1. For example, the 4 th axiom derivation results
from the rule application on the 3rd axiom derivation.
Note it is somehow similar to the method deriveAxiom:withRule:give:atLevel: defined
in the previous chapter.
4. Examples 227

Script 24.9

| fib |
fib := LSystem fibonacci.
fib deriveAxiomAtLevel: 1
returns ’A’
self deriveAxiomAtLevel: 2
returns ’AB’
self deriveAxiomAtLevel: 3
returns ’ABA’
self deriveAxiomAtLevel: 5
returns ’ABAABABA’

3.4 Discussion about Object-Oriented Modeling


We would like to stress that defining the axiom derivation as the behavior of the L-System object is what
distinguish object programming from procedural programming. In procedural programming, some data
structures representing the rules and an L-System would have been defined and some procedures would
have accessed them to derive the axiom. Here, the key point is that the structure is only accessible by
the L-System itself. Each class defines the structure that it manipulates, it controls the access to such
information and specifies the behavior that can be invoked to manipulate these data. Moreover, the class
with its structure and behavior is a single entity. Note that the internal representation of an L-System is not
relevant because as a client we are only interested by the behavior of an L-System, i.e., the specification of
the system and the axiom derivation.

3.5 Invoking the Graphical Interpretation


Define the method drawAtLevel:length:angle:. Such a method should create a turtle and ask it to
interpret (using the method interpret:length:angle:) the result of the method deriveAxiomAtLevel:.

4 Examples
Now you have all the pieces of the puzzle and are in position to play with the L-Systems. First start to
express all the L-Systems shown in the previous chapter with the new way of implementing L-Systems.
For example, the following method shows the implementation as class methods the L-System shown
by the Figure 23.4. Define such a method as class methods of LSystem in a category ’examples’. We
chose to write this L-System as a method and not simply as a script so that we are able to save it with the
class and we do not have to retype it again and again.
Method 24.8

LSystem class>>simpleBasicOne
"self simpleBasicOne"

| lsys |
World clearTurtleTrails.
lsys := self new.
lsys axiom: ’F-F-F-F’.
lsys add: (LSRule leftPart: ’F’ rightPart: ’FF-F-F-F-F-F+F’).
lsys drawAtLevel: 4 length: 2 angle: 90.

You can be willing to define the following L-Systems as class methods.

-2001 Stéphane Ducasse ([email protected])


228 Advanced L-Systems as Objects

Quadratic Koch island (Figure 24.2)


Axiom F-F-F-F
Angle 90 degree
Rule F → F+FF-FF-F-F+FF-F-F+F+FF+FF-F

Islands (Figure 24.3)


Axiom F+F+F+F
Angle 90 degree
Rule f → ffffff
Rule F → F+f-FF+F+FF+Ff+FF-f+FF-F-FF-Ff-FFF

Figure 24.3: Islands. n = 2, angle = 90, Axiom: F+F+F+F, F → F+f-FF+F+FF+Ff+FF-f+FF-F-FF-Ff-


FFF, f → ffffff

5 L-System containing Subfigures


As the vocabulary that we used so far is limited to F, f, +, and -, it is difficult to produce rich fractals, or
curves whose generation is based on multiple rules. Indeed, as the idea behind rewriting rules is to rewrite
the path followed by the turtle into subpaths, we can only have one rule having F as left part and one rule
having f. The L-System named Island presented in Figure 24.3 above is one of the rare one. Experiment a
bit to see if you can obtain nice figures by rewriting + and -.
To solve this problem, L-Systems are simply extended to support the definition of subfigures. The
idea is that new symbols like R or L can appear now in the left or right part of a rule, then once the
derivation is finished the symbols are replaced by a sequence of the original vocabulary elements, the
simplest replacement being F. The new symbols represent simple figures that are this way repeated all over
the place. Try for example to understand the figures R and L of the Hilbert L-Systems.
The following L-System expresses the Sierpinski gasket as shown by Figure 24.4. The rules are ex-
pressed in terms of two new symbols R and L that after the derivation are considered equivalent to F.
5. L-System containing Subfigures 229

Sierpinski Gasket (Figure 24.4)


Axiom R
Angle 60 degree
Rule L → R+L+R
Rule R → L-R-L
Figure R→F
Figure L→F

Figure 24.4: Sierpinski Gasket. n = 7, angle = 60, Axiom: R, L → R+L+R, R → L-R-L, figures: L → F, R
→ F.

The following L-System expresses the Hilbert curve. The rules are expressed in terms of two new
symbols R and L that after the derivation are expanded into their respective definition.

Hilbert Curve
Axiom L
Angle 90 degree
Rule L → +RF-LFL-FR+
Rule R → -LF+RFR+FL-
Figure R → -F+F+F-
Figure L → +F-F-F+

If look at the derivation of the axiom of the Hilbert curve at level 2 before expanding the figures we
obtain the following result: +-LF+RFR+FL-F-+RF-LFL-FR+F+RF-LFL-FR+-F-LF+RFR+FL-+. Then af-
ter applying the figures we get: +-+F-F-F+F+-F+F+F-F-F+F+F-+F+F-F-F+-F-+-F+F+F-F-+F-F-F+F+F-F-
F+-F-F+F+F-+F+-F+F+F-F-+F-F-F+F+F-F-F+-F-F+F+F-+-F-+F-F-F+F+-F+F+F-F-F+F+F-+F+F-F-F+-+
in which there is no symbol representing figures.

5.1 Analysis
From the previous examples, we see that we need:

◦ to specify that a certain element should be replaced by a sequence of elements representing the figures.
For example, in the Hilbert curve, R has to be replaced by -F+F+F-.
◦ to first derive the axiom a certain number of times and then replace the elements representing figures
by their definition.

5.2 Implementing subfigures


We would like to have a new class to represent L-System with subfigures because we do not want to clutter
the LSystem class with functionality that only a subset of the L-Systems use. However, we want to reuse

-2001 Stéphane Ducasse ([email protected])


230 Advanced L-Systems as Objects

the functionality defined by the class LSystem. So we define the new class as subclass of LSystem and
specialize its behavior where it is needed as shown by the Figure 24.5.

As we want to show you another way to represent simple rules (subfigures are indeed really similar to
rules), we chose not to use the LSRule class to represent the figures but to use a dictionary to store the
definition of figures. As an exercise we suggest you to use rules to represent figures. So let us look at a
dictionary!

Figure 24.5: Relationships between the different classes participating in the LSystem application. (For the
Reviewers: we will explain somewhere the meaning of the signs.

Using a Dictionary. A dictionary or hash table is a data structure that allows one to store and access
information based on keys. It avoids to go over long collections. Given a key and a value we can add the
value into the dictionary and later retrieve such an information by using the associated key. The key can be
any object, so it can a symbol, a character, a string or even a turtle. The following script shows data can be
added and accessed form a dictionary.

In addition we show you some functionality that you may need for your implementation. It is possible
to specify an action that is performed if the key is not found in the dictionary using at:ifAbsent:.
[nil] is the action that returns nil. keys is the message to obtain all the keys contained in a dictionary.
We suggest you to browse the class Dictionary and try some methods.
5. L-System containing Subfigures 231

Script 24.10

| dict |
dict := Dictionary new.
dict at: #caro put: Turtle new.
dict at: #F put: 2.
dict at: ’F’ put: 3.
dict at: #caro. returns a turtle
dict at: #F. returns 2
dict at: ’F’. returns 3
dict at: ’A’ ifAbsent: [nil]. returns nil
dict keys. returns #caro #F $F

5.3 An example of Subfigures


The following method shows how the Hilbert curve L-System is defined using the class LSystemSubfigures
Method 24.9

LSystemSubFigures class>>hilbert
"self hilbert"

| lsys |
lsys := self new.
lsys axiom: ’L’.
lsys add: (LSRule leftPart: ’L’ rightPart: ’+RF-LFL-FR+’).
lsys add: (LSRule leftPart: ’R’ rightPart: ’-LF+RFR+FL-’).
lsys addSubFigure: ’L’ withDefinition: ’+F-F-F+’.
lsys addSubFigure: ’R’ withDefinition: ’-F+F+F-’.
lsys drawAtLevel: 3 length: 4 angle: 90.
^ lsys

5.4 Class Definition and Instance Initialization


Define a class, named LSystemSubfigures, that inherits from LSystem and have an instance variable
that represents the subfigures dictionary.
Define the method initialize in the category ’initialize-release’. Do not forget to
invoke the initialize method defined on the superclass.

5.5 Discussion about initialize


The method initialize defined on the class LSystemSubFigures is invoked when a new instance
is created because the method new of the superclass invokes it. Let us analyse what’s really happen!

For Reviewers: Do be done and may be moved elsewhere

5.6 Defining and Computing with Subfigures


Define the method addSubFigure: aString withDefinition: anotherString that adds
the subfigure definition as shown by the method 24.9. Hints: Look at Dictionary class.
Define the method expandSubFiguresIn: aString that replaces in a string all the elements
representing a figure by all the figure constituants. Here follow some tests to help you.
-2001 Stéphane Ducasse ([email protected])
232 Advanced L-Systems as Objects

Script 24.11

| lsys |
lsys := LSystemSubFigures hilbert.
lsys expandSubFiguresIn: ’L’.
returns ’+F-F-F+’
lsys expandSubFiguresIn: ’ALBL’.
returns ’A+F-F-F+B+F-F-F+’
lsys expandSubFiguresIn: ’AB’.
returns ’AB’
lsys expandSubFiguresIn: ’ALBR’.
returns ’A+F-F-F+B-F+F+F-’

Now once the axiom is derived n times we need to invoke the method expandSubFiguresIn:.
What we need is to specify that the derivaAxiomAtLevel: method have to execute normally and
then expand the figures. To do so we just have to redefine the method deriveAxiomAtLevel: on the
class LSystemSubFigures to invoke the default behavior of this method defined on LSystem, keep
the result returned and pass it to expandSubFiguresIn:.

5.7 Another example of L-System


The Dragon Curve shown in Figure 24.6 is a well-known recursive curve. It can be expressed as follow:

Dragon Curve (Figure 24.6)


Axiom L
Angle 90 degree
Rule L → L+R+
Rule R → -L-R
Figure R→F
Figure L→F

Figure 24.6: n = 7, angle = 60, Axiom: L, L → L+R+, R → -L-R, figures: L → F, R → F.


5. L-System containing Subfigures 233

5.8 FASS Curves


Now you can experiment with the following LSystems that produce FASS curves (space-Filling, self-
Avoiding, simple and self-Similar). The particularity of these definition is that the new symbols are simply
ignored. In our implementation we can replaced them by an empty string. Note that if you implemented
the interpret:length:angle: method on the class Turtle to skip elements not belonging to the
vocabulary of the turtle you can avoid to define such emptying figures.

Figure 24.7: FASS1: n = 3, length = 4

FASS1 (Figure 24.7


Axiom -L
Angle 90 degree
Rule L → LF+RFR+FL-F-LFLFL-FRFR+
Rule R → -LFLF+RFRFR+F+RF-LFL-FR
Figure R→
Figure L→

Figure 24.8: FASS2: n = 2, length = 6

FASS2 (Figure 24.8)


Axiom -L
Angle 90 degree
Rule L → LFLF+RFR+FLFL-FRF-LFL-FR+F+RF-LFL-FRFRFR+
Rule R → -LFLFLF+RFR+FL-F-LF+RFR+FLF+RFRF-LFL-FRFR
Figure R→
Figure L→

FASS3 (Figure 24.9)


Axiom -L
Angle 90 degree
Rule L → L+F+R-F-L+F+R-F-L-F-R+F+L-F-R-F-L+F+R-F-L-F
-R-F-L+F+R+F+L+F+R-F-L+F+R+F+L-F-R+F+L+F+R-F-L+F+R-F-L

-2001 Stéphane Ducasse ([email protected])


234 Advanced L-Systems as Objects

Figure 24.9: FASS3: n = 3, length = 2

Rule R → R-F-L+F+R-F-L+F+R+F+L-F-R+F+L+F+R-F-L+F+R+F
+L+F+R-F-L-F-R-F-L+F+R-F-L-F-R+F+L-F-R-F-L+F+R-F-L+F+R
Rule K → L+F+R-F-L+F+R-F-L-F-R+F+L-F-R-F-L+F+R-F-L-F
-R-F-L+F+R+F+L+F+R-F-L+F+R+F+L-R-F+F+L+F+R-F-L+F+R-F-L
Figure R→
Figure L→
Figure K→

Figure 24.10: Peano Curve: n = 1 and Quadratic Gosper Curve: n = 2

Peano Curve (Figure 24.10)


Axiom S
Angle 90 degree
Rule S → SFZFS+F+ZFSFZ-F-SFZFS
Rule Z → FSFZ-F-SFZFS+F+ZFSFZ
Figure S → F+F+F-F-F
Figure Z → F-F-F+F+F

Quadratic Gosper Curve (Figure 24.10)


Axiom -R
Angle 90 degree
Rule L → LL-R-R+L+L-R-RL+R+LLR-L+R+LL+R-LR-R-L+L+RR-
6. Other Implementation Strategies 235

Rule R → +LL-R-R+L+LR+L-RR-L-R+LRR-L-RL+L+R-R-L+L+RR
Figure L→F
Figure R→F

Figure 24.11: Towers. The following curves shows the derivation of an L-System created using subfigures
at level 0,1,2,3,and 4. Note that the length used is 2 pixels except for the last one where we used 1 pixel.

Experiment 24.1
Try to define a LSystem that produces the previous picture.

6 Other Implementation Strategies


We propose you some alternative implementation solutions.

Experiment 24.2
Implement the figures as LSRule instances. The implementation should be then similar to the one of plain
rules.

Experiment 24.3
Without changing the interface of the class LSystem (method names and arguments) use a dictionary
instead of an ordered collection to store the LSRule.

Using an array. As we mentioned earlier, instead of using two instance variables for the left and right
part of a rule you can also use only one instance variable holding an array. Here using an array is not a
really good idea because (1) the class definition will lose readibility, (2) accessing one or the other parts
requires an extra indirection, and (3) the extra array created does not bring anything.
In fact, using instance variable holding an array or a collection is definitively better suited when the
size of objects held by the collection is changing for instances to instances of the class. This is the case
with the instance variable rules of the class LSystem, in such a case each L-System can have a different
number of rules.
Nevertheless, this is a good exercise for you. It shows you that the implementation of a class can change
without breaking the behavior of such a class. This way encapsulation prevents having too much to change
when a different implementation decision is required.

-2001 Stéphane Ducasse ([email protected])


236 Advanced L-Systems as Objects

Change the implementation the rule class without changing the functionality it provides to use an array.
Hints: you may pay attention to initialize the instance variable holding the array with an empty array of
size two. Read 2.4.

7 Further Readings
[?] is a first book in the literature that presented the concept of responsibility driven design. It is interesting
to see how from objects are identified from a textual description.
[?] presents a set of heuristics to support you during the design of your application. The concepts are pre-
sented using C++, however the heuristics are general and applicable in any object-oriented language.
Project 25
Modeling Plant Growth
with L-Systems

For Reviewers: I would like to present 3D like trees, however this implies to
introduce so called generic L-Systems, which requires an interpretation mech-
anism (parameters and expressions should be evaluated) instead of a simple
string replacement. Moreover the turtle should be able to evolve in a 3d space.
I do not know if this is worth the complexity. One the other side I could try to
use the 3d engine of Squeak, and show some problems and their solutions. I
suspect that such an extension would take another chapter. Do you think that
this is interesting? Would you like to read that?

-2001 Stéphane Ducasse ([email protected])


238 Modeling Plant Growth with L-Systems

Figure 25.1: Plant1. n = 5, 25.7 degree, F, F → F[+F]F[-F]F

Stef

In this chapter we show how L-Systems are used to model plants. We present how theorical biologists,
like Lindenmayer, enhanced simple L-Systems to describe branching. Then we extend the Turtle class
to support the needed behavior by creating a new class of Turtle.

1 L-System with Branching


To represent plants or trees it is necessary to be able to model branches. To that purpose, theoritical
biologists introduced in L-System the notion of branching. The idea is that to produce a branch the current
state of the turtle should first be stored, then the turtle draws the branch hence modifying its state, then the
state before branching is restored and another branch with the same approach can be drawn. Note that as
branch can contains other branches, we have to be able to store a collection of turtle states and restore then
in opposite order they have been stored: we need to store them into a stack.
To introduce such an idea into an L-System, the biologists introduced the notion of storing the state of
a turtle and restoring it. Two new symbols are added:

[ pushes the current turtle state into the stack of turtle state.

] pop the last stored state of the the stack and change the current turtle state accordingly.

Here is the definition of the L-System that is shown in Figure 25.1.

Plant 1. (Figure 25.1)


Axiom F
Angle 25.7 degree
Rule F → F[+F]F[-F]F

From the implementation point of view, this L-System is created and drawn as follow:
1. L-System with Branching 239

Figure 25.2: Plant 2. n = 5, 20 degree, FF → F[+F]F[-F][F]

Script 25.1 (Using branching in a L-System)

| lsys |
lsys := self new.
lsys axiom: ’F’.
lsys add: (LSRule leftPart: ’F’ rightPart: ’F[+F]F[-F]F’).
lsys drawAtLevel: 4 length: 6 angle: 22.5

1.1 Some examples


Here we propose you some L-Systems defining plants.

Plant 2. (Figure 25.2)


Axiom F
Angle 20 degree
Rule F → F[+F]F[-F][F]

The following L-System produces the first picture of this chapter after 4 iterations.

Plant 3. (Chapter Picture)


Axiom F
Angle 22.5 degree
Rule F → FF-[-F+F+F]+[+F-F-F]

In the following L-Systems, the second rule is there to control the length of the branches. This is a bit
limited because the length can only be a integer multiple of the distance the turtle moves forward.

Plant 4.
Axiom X

-2001 Stéphane Ducasse ([email protected])


240 Modeling Plant Growth with L-Systems

Figure 25.3: n = 5, 22.5 degree, X, F → FF, X → F-[[X]+X]+F[+FX]-X

Angle 20 degree
Rule X → F[+X]F[-X]+X
Rule F → FF
Plant 5.
Axiom X
Angle 25.7 degree
Rule X → F[+X][-X]FX
Rule F → FF
Plant 6. (Figure 25.3)
Axiom X
Angle 22.5 degree
Rule X → F-[[X]+X]+F[+FX]-X
Rule F → FF

2 A Turtle with Memory


To implement the semantics of
and
, i. e., to store or restore the state of a turtle, we have to implement some new behavior. This behavior,
maintaining a collection of turtle states, storing or restoring them is definitively a new responsibility that
has to do with turtle behavior, so we will add this new behavior to the class Turtle. In fact, we prefer
to extend the class Turtle by creating a subclass of Turtle because not all the turtles have to have this
specific behavior. The Figure 25.4 presents the solution we propose you.
2. A Turtle with Memory 241

Figure 25.4: Extending the Turtle class by creating a subclass: TurtleMemory.

2.1 The strategy


There are different ways to implement the new behavior that we need. However, the simplest one is to
see that the states that we want to store or restore behave like a stack of plates. Stacks are one of the
elementary data-structures like collection, hash-table, or queues that any programmer has to master. Let’s
illustrate how a stack works: if we store S1, S2 and S3 three states on a stack in the chronological order,
then the top of the stack is S3 and the bottom S1. Stacks are last in first out structure: S1 will be the lastest
state to be popped out the stack. This is exactly what we want: we want to store the state of a turtle before
drawing a branch (which may contain other branches) and come back to this state later. Popping a stack
means that the top of the stack is removed and that the element below becomes the top of the stack. Pushing
an element into the stack means that the element becomes the top of the stack.
Script 25.2 (Behavior of a stack)

stack push: s1.


stack push: s2.
stack push: s3.
stack pop. returns s3
stack push: s4.
stack pop. returns s4
stack pop. returns s2

For Reviewers: Here an illustration is missing

2.2 Implementing a stack


If you look in Squeak using a class browser for a class whose name contains Stack or using a method
finder, you will not find a class directly implementing a stack of objects. Now if you analyze a bit a stack
you can see that: (1) a stack is a kind of ordered collection. Its elements are ordered based on the order
in which they are pushed on the stack and (2) we are able to add or remove dynamically elements. These
two observations make the instances of OrderedCollection good candidates for representing a stack.
Now we have to see how pushing an element and popping the stack can be implemented. Let’us take the
following convention: the top of the stack is the last element of the collection.

-2001 Stéphane Ducasse ([email protected])


242 Modeling Plant Growth with L-Systems

Do it. Look at the methods defined on the class OrderedCollection and find two methods that can
be used to implement the following methods:

◦ one, named push:, that could be used to implement the addition of an element on the top of the stack.
According to the convention we chose, the element should be added at the end of the collection. Note
that as ordered collections grow dynamically we do not have to check if we overpass the size of the
collection.
◦ another method, named pop, that could be used to implement the action to pop the stack, i.e., returns
the top element and remove it from the stack.

2.3 Subclassing the class Turtle


The state of a turtle that we need to store and restore is not represented by a single object but composed by
two objects: a point representing its position and a number representing its direction. So we have several
possibilities to represent a stack of turtle state: we can have one stack for the position and one stack for
the direction and manipulate them in synch, or bundling together the position and direction using a pair of
objects and using one single stack. We chose the second approach.

About Association. To create a pair of objects, we create an instance of the class Association
by sending to any object the message -> with as argument the other object we want to associate with.
Then accessing the elements of the association is performed by sending the messages key or value to
the association. The Script 25.3 illustrates the creation and access to an association. For more information
browse the class Association.

Script 25.3 (Example of association use)

| anAssociation |
anAssociation := #caro -> 3.
anAssociation key returns #caro
anAssociation value returns 3

Class Definition. Define a class named TurtleWithMemory subclass of the class Turtle. This
class has an intance variables named stateStack that represents a stack of turtle state.

For Reviewers: This part will certainly migrate to a chapter going deeper into
this topic

2.4 Instance Variable Initialization


In Smalltalk, instance variables are initialized with the nil object, instance of the class UndefinedObject
— this means that they value is the object nil. That’s why before using an instance variable, we have to
initialize it with a value consistent with its future use. There is no predefined way in Smalltalk to initialize
instance variables. One way is to invoke a method often named by convention initialize that sets the
value of the instance variables as soon as the instance is created. However, what is important is that such
an initializing method should be systematically invoked on the newly created object, else we could forget
to invoke it and obtain an object in an inconsistent state.
To ensure that an initializing method is automatically invoked, we can specialize the class method new,
whose responsibilities is to create new instances of the class to which new is sent. There are two key points
not to forgot: (1) the creation of instance should be invoked, and (2) the newly created instance should
returned. A method new defined on an hypothical class XXX would look as the following:
2. A Turtle with Memory 243

Method 25.1

XXX class>>new

|inst|
inst := super new.
inst initialize.
^ inst

First, the new instance is created by invoking the method new, then an initializing method, called
initialize is invoked on this newly created instance and finally the instance is returned. Note that this
method is equivqlent to the following one:
Method 25.2

XXX class>>new

^ super new initialize

The method initialize can initialize the value of some instance variables of the instance as follow
two forms are possible using direct access to instance variables or using accessors if they have been defined:
Method 25.3

XXX>>initialize

myInst1 := 0

Method 25.4

XXX>>initialize

self myInst: 0

For Reviewers: Until there the part will be moved in another place

Initializing turtle with memory. Before reproducing the initialization schema, we should look at the
superclasses of the class TurtleWithMemory to check if they do not already redefine the method new.
In such a case there is no need to redefine it again and we just need to redefine the initialize method.
As you can find, the superclass Morph redefine the method new as follow, so there is no need to redefine
it.
Method 25.5

Morph class>>new

^ super new initialize

Define the method initialize to set the value of the instance variable stateStack to an empty
ordered collection as follow.

-2001 Stéphane Ducasse ([email protected])


244 Modeling Plant Growth with L-Systems

Method 25.6

TurtleWithMemory>>initialize
"initialize the state stack"

super initialize.
stateStack := OrderedCollection new.

As a TurtleWithMemory instance has all the behavior that a normal turtle has plus the extra behavior
necessary to store and restore turtle’s state, we have to invoke the initialize method defined in the
superclass of TurtleWithMemory, i.e., Turtle or its superclass. This is the purpose of the expression
super initialize. Then the instance variable specific to the class TurtleWithMemory is initialized
to hold an empty collection, instance of OrderedCollection that represents the turtle state stack.

Push/Pop. Using the knowledge you gain about stack implementation, define the following methods:

◦ pushState adds to the stack of turtle states the current state of the turtle.
◦ popState returns the top element of the stack and make the subsequent element the new top. Do
not check if the stack is empty.
◦ isStateLeft checks whether the stack is empty.
◦ restoreLastState changes the current turtle state to be the last ones that have been stored in the
state stack.

Extending the L-System Interpretation. Now you just have to extend the method interpretSymbol:
aChar length: len angle: degree to take into account the interpretation of symbols: [
and ]. Think about reusing the method interpretSymbol:length:angle: defined on the class
Turtle.

Experiment 25.1
Chose as convention that the beginning of the collection is the top of the stack and reimplement the func-
tionality. Note however that the implementation of ordered collection makes more efficient the first solu-
tion.

Experiment 25.2
Chose to represent the turtle state stack as two stacks: one stack for the position and one for the direction.

3 Stochastic L-Systems
Up until now a given L-System produces always the same plant in a deterministic manner. There is no
random decision. To introduce more realistic and different plants, biologists introduce in L-System the
notion of random selection of a rule, hence the term stochastic L-Systems. The idea is simple, every rule
is given a probability to be selected and while checking if a rule is applicable the L-System selected them
according to this probability.

Axiom F
Angle 25.7 degree
Rule F .33 → F[+F]F[-F]F
Rule F .33 → F[+F]F
Rule F .34 → F[-F]F
3. Stochastic L-Systems 245

We will not propose any solution and let you think about the solution. However to help a bit here are
some points that you should think of:
◦ Do the rule have equal probability? This can have an impact on when to assign a rule its probability.
Once the rule is created or once all the rules have been added. The simplest solution being to assign
individual probability to each rule and let this responsibility to the programmer. A next step is then to
let the system computes the probability to the rules.
◦ Is a probability assign to a rule? Presumbly yes, the probability can be just an extra property of a rule.
Take care to initialize it to a valid value.
The following expression shows how a random number between 0 and 10 excluded can be generated.
Script 25.4 (Random Number)

10 atRandom

-2001 Stéphane Ducasse ([email protected])


246 Modeling Plant Growth with L-Systems
Project 26
Parametric L-Systems

One of the limits of L-Systems that we have seen so far is that we cannot manipulate any parameters
like angle or length to be drawn. The only way we have was to use rules to rewrite a forward command
by several ones. Parametric L-Systems propose an answer to such a problem by providing a way to pass
parameters to the rule and manipulating them. In this chapter, we propose you to implement this new kind
of L-Systems. However, we just do not restart from scratch a new implementation but reuse some of the
previous implementation. Let us first look at the new aspects of parametric L-Systems.

1 Parametric L-Systems
The last kind of L-Systems we would like to use is called parametric L-System (pL-Systems). Parametric
L-Systems allows one to express finer plant growth or fractals. The idea is that a rule can specify arguments
or parameters that are evaluated and manipulated.
Let us look at two examples: the first parametric L-System produces the first picture of this chapter, the
second one the figure 26.1.
T
Axiom A(0)
Angle 88 degree
Rule A(d) → F(1)[+A(0)][-A(0)]
Rule F(d) → F(d * 1.456)

-2001 Stéphane Ducasse ([email protected])


248 Parametric L-Systems

Leaf 1 (Figure 26.1)


Axiom A(0)
Angle 45 degree
Rule A(d): d > 0 → A(d - 1)
Rule A(d): d = 0 → F(2)[+A(1)][-A(1)]F(2)A(0)
Rule F(d): – → F(d * 1.33)

Figure 26.1: Parametric L-System: Leaf 1, Level = 10.

1.1 Examples of pL-System Derivation


Deriving the axiom of the T pL-System shows how a parameter value is manipulated. In the second rule,
F (d) → F (d ∗ 1.456) after two derivations, F (1) is transformed into F (1.1456), after three derivations
F (1) is transformed into F (2.119936) where 2.119936 is 1.456 * 1.456.

Axiom A(0)
Level 1 → F(1)[+A(0)][-A(0)]
Level 2 → F(1 * 1.456)[+F(1)[+A(0)][-A(0)]][-F(1)[+A(0)][-A(0)]]
Level 3 → F(1.456 * 1.456)[+F(1 * 1.456)[+A(0)][-A(0)]][-F(1 * 1.456)[+A(0)][-A(0)]]

Deriving the axiom of the Leaf 1 pL-System shows how the rule selection takes into account the value
of the parameter. First, as the parameter d of A(d) is equal to 0, the second rule is selected. The second
level is obtained by applying the rules 3, 1, 1, 3, and 2 on the result obtained previously. The third level is
obtained by applying the rules 3, 2, 2, 3, 3, 1, 1, 3, and 2 on the result obtained previously.
Note that the first rule acts as a delay for the application of the second rule. Indeed A(1) has to be
transformed in A(0) before the second rule is applied.

Axiom A(0)
Level 1 → F(2)[+A(1)][-A(1)]F(2)A(0)
Level 2 → F(2.66)[+A(0)][-A(0)]F(2.66)F(2)[+A(1)][-A(1)]F(2)A(0))
Level 3 → F(3.5378)[+F(2)[+A(1)][-A(1)]F(2)A(0)][- F(2)[+A(1)][-A(1)]F(2)A(0)]F(3.5378)
F(2.66)[+A(0)][-A(0)]F(2.66)F(2)[+A(1)][-A(1)]F(2)A(0))
2. Scanning, Representing and Interpreting New Expressions 249

1.2 Analyzing a Parametric L-System


Parametric L-Systems are more complex than simple L-Systems, pL-System rules have more functionality.
Let us analyse such differences:

◦ The rule left part is not simply a symbol but composed by a symbol and a parameter. In the example,
F(d) → F(d * 1.456) means that expressions with the symbol F and an argument, named d, should be
replaced by F(d * 1.456) where d is substituted by its value.
◦ A rule may have an associated condition. Such a condition is expressed in terms of the rule left part
parameter and should be true for selecting the rule. Note that such a condition can be omitted. In such
a case this means that the condition is true. For example, the second rule of the last example will only
be selected when d’s value is zero.
◦ In the right part of a rule the vocabulary of the rule is not anymore a simple symbol like before but
a symbol with which arguments are associated. Such arguments can in fact be simple arithmetic
expression referring the left part rule parameters.
◦ When a turtle interprets the derivation of such L-Systems it has to take into account the parameter
values as arguments for its methods. Hence,F(30) has to be interpreted as go: with 30 as argument.

Definition
From now we call a composed fragment of the L-System: a symbol, its
associated argument and expression.

F (d ∗ 1.456) is a composed fragment composed by a symbol F and an expression d ∗ 1.456,


and requiring one argument d. [ is then just a fragment.

We have several problems to address like how to represent the composed fragments, the condition
associated with a rule, and how to perform the interpretation of the arguments. Note that in the solution,
we limit ourselves to single parameter L-Systems and let you as an exercise to handle multiple parameters.

2 Scanning, Representing and Interpreting New Expressions


To interpret a composed fragment, we first have to know how many arguments are required and what are
their values. There are different ways to solve this problem like reading one by one the fragment characters
(this is named scanning and parsing in computer science jargo) and creating objects that represent them
(usually this would form some tree structure) and defining an interpreter based on these objects that would
know how to produce new values. This is the way to treat a lot of interpretation problems, for example
Squeak itself is based on such an approach, when a method or a class is defined, it is scanned, parsed, a
tree is created then some byte-code (the internal representation that the Squeak interpreter named a virtual
machine knows how to interpret) representing the methods are generated. However, for our simple problem
we would to have the simplest solution that works.
We need a way to distinguish a composed fragment and its arguments from the other fragments. With
the current definition a composed fragment, i.e., F (d), we would have to read one by one the characters that
compose the string representing a fragment and look for open and close parenthese to identify arguments
associated with the first character read. This is boring....
As Squeak has to parse Smalltalk expressions, before trying to implement a heavy solution to this
problem, we should try to see if the functionality we are looking for does not already exist in Squeak. The
method scanToken: of the class Scanner offers the functionality we are looking for.

-2001 Stéphane Ducasse ([email protected])


250 Parametric L-Systems

Script 26.1 (A first attempt at using the default scanner)

Scanner new scanTokens: ’F(1)[+A(0)][-A(0)]’.


returns #(#F #(1) #[ #+ #A #(0) #] #[ #- #A #(0) #])

As shown by the previous example, the method scanToken: of the class Scanner offers us nearly
what we want, it decomposes the string into symbols and the fragment arguments are contained into arrays.
Still we have to take care about the symbol preceding an array and this is annoying. We would like to have
an array representing the composed fragment as this is easier to manipulate.
A simple way to solve this problem is to slightly change the representation of an L-System the following
way: F(d) changed into (F d). For example representing the expression F(1)[+A(0)][-A(0)] as ’(F 1)[+(A
0)][-(A 0)]’ simplifies a lot the problem. Now invoking the same method provides us what we want.

Script 26.2 (Scanning composed fragments.)

Scanner new scanTokens: ’(F 1)[+(A 0)][-(A 0)]’.


#(#(#F 1) #[ #+ #(#A 0) #] #[ #- #(#A 0) #])
Scanner new scanTokens: ’A x y’.
#(#A #x #y)
Scanner new scanTokens: ’(F d * 1.456)’.
#((#F #d #* 1.456))

The method scanToken: returns an array containing all the fragments it contains as arrays. This way
composed fragments are recognizable. The second example shows that scanning a string whose items are
separated by spaces still returns an array. Finally the last example shows that if the string items represent a
number, this number and not a string representation of it, is put into the resulting array. The last example
shows that a the method always returns an array, here with a single element containing an array.
With this new representation you have to take care that spaces are important. The two following ex-
pressions show that without space the scanner assimilates (A 0) to one single symbol A0, so A(0) would
not been interpreted correclty.

Script 26.3 (Showing problem when forgetting space character.)

Scanner new scanTokens: ’(F 1)[+(A 0)][-(A 0)]’.


#(#(#F 1) #[ #+ #(#A 0) #] #[ #- #(#A 0) #])
Scanner new scanTokens: ’(F1)[+(A0)][-(A0)]’.
#(#(#F1) #[ #+ #(#A0) #] #[ #- #(#A0) #])

If we want to take benefit of this functionality we have to decide how the axiom has to be specified and
the left part of rule as well. This is what we do in the next section.

2.1 Choosing the public definition of a pL-System

Before going deeper into the implementation we have to choose how we want the user to specify a pL-
System. The following script presents the solution we propose. Note that this is not the only one.
3. Parametric Rules 251

Script 26.4 (The Leaf 1 pL-System)

| lsys |
lsys := ParametricLSystem new.
lsys axiom: ’(A 0)’.
lsys angle: 45.
lsys add: (ParametricLSRule
leftPart: ’(A d)’
rightPart: ’(A d - 1)’
condition: [:each | each second > 0]).
lsys add: (ParametricLSRule
leftPart: ’(A d)’
rightPart: ’(F 2)[+(A 1)][-(A 1)](F 2)(A 0)’
condition: [:each | each second = 0]).
lsys add: (ParametricLSRule
leftPart: ’(F d)’
rightPart: ’(F d * 1.33)’
It shows that we chose the uniformity for the definition of the axiom and the left part of a rule. However,
you have to take care because the axiom, like the right part of a rule, is a collection of fragments, while the
left part represents just one single fragment. You will have to take into such a conceptual difference when
dealing with the left part of a rule. Let us start to look at the new class of rules.

3 Parametric Rules
The rules defining a pL-System are different from the rules of basic L-System. To represent such a different
behavior we create a new class of rule and specialize rule behavior.
Script 26.5 (Example of parametric rule)

|rule|
rule := ParametricLSRule
leftPart: ’(A d)’
rightPart: ’(A d - 1)’
condition: [:each | each second > 0].
rule leftPart.
returns #(A d)
rule rightPart.
returns #((A d -1))

Do it! Define a subclass of LSRule named for example ParametricLSRule. This class should have
an instance variable to represent the condition of that a rule may have. Define the methods condition
and condition: that access and set the condition of a rule.
Using the information we gave in section 2, define a method named convertToArray: in category
’tools’ that parses a string into an array and returns it. This method should be used by the accessors
methods leftPart: and rightPart:. Indeed even if these accessors have been defined on the class
LSRule, they have to be redefine to deal with the fact that in ParametricLSRule the string, e.g., ’(A
d -1)’, has to be parsed and converted into an array as we explained in section 2. Note that the definition
of the methods leftPart and rightPart does not have to be changed, so it can be reused from the
superclass. The definition of rightPart: is straighforward, so do it! You should pay attention for the
definition of leftPart: because as shown by the previous script, the left part of a rule is not a collection
of fragments but a single fragment, so we would like to store in a rule left part only the fragment. Note that
this is a decision we took to ease the manipulation of rule left part, we could have let it defined as an array
of a single element and always have to know this fact while checking if the rule matches a given fragment.

-2001 Stéphane Ducasse ([email protected])


252 Parametric L-Systems

3.1 Representing Condition


An easy way to represent the condition of a rule is to use a block. A block is nearly like a method in
the sense that it can have arguments, return a result per default the value of their last instruction, can have
temporary variables, and contains a sequence of instructions. Blocks and methods differs in that (1) a block
does not have a name, (2) can be assign to variable and passed as arguments and (3) is executed using a
different way than method.
Let us illustrate these points. Imagine that we want to have a block representing the function that adds
2 to a certain number.
Script 26.6 (Blocks definition and execution)

| b1 b2 b3 |
b1 := [ 3 + 5]
b1 value.
returns 8.
b1 value.
returns 8.

b2 := [ :x | x + 2].
b2 value: 4.
returns 6
b2 value: 1.
returns 3

b3 := [ :x :y | x + y]
b3 value: 4 value: 3.
returns 7
A block is defined and delimited by [ and ]. A block can have zero, one or more arguments. Arguments
are defined between [ and | and are prefixed by :. A block is defined then later on we can execute it. To
execute a block we have to invoke the method value when the block has no argument, value: when
the block has one argument, value:value: when the block has two arguments... this schema goes until
four arguments after you have to use valueWithArguments: and passes it an array representing the
values.
The rule condition [:each | each second > 0] taken from the rule definition shown in Script 26.5
expresses the fact that the second element of the fragment has to be greater than 0. Hence the following
expressions can be evaluated.
Script 26.7

| b |
b := [:each | each second > 0].
b value: #(F 3).
returns true
b value: #(F -1).
returns false

3.2 Parametric Rule Match


The matching behavior of parametric rules differs from the one of basic rules. First, remember that an
LSRule left part was coded as a string. Second, the method LSRule»doesMatch: was just to check
whether the first character of the passed string was equal to the first character of the left part.
Script 26.8

LSRule leftPart: ’A’ rightPart: ’AB’


3. Parametric Rules 253

Method 26.1

LSRule>>doesMatch: aString
^ leftPart first = aString first

Such a behavior is not enough for parametric rule. Indeed, we have to check if the condition is also
true. However, we do not have to rewrite everything because String is also a collection as is Array. So
the behavior defined on the class LSRule still works on ParametricLSRule instances.
The behavior of the method ParametricLSRule»doesMatch: is then to check whether the first
elements of the left part and the fragment are the same (this can be done by calling the method doesMatch:
defined on the superclass) then to check if the condition associated with the rule returns true when valued
with the fragment been matched. Implement the method ParametricLSRule»doesMatch:.

3.3 ParametricLSRule Instance Creation Protocol


As shown in the Script 26.5, creating a rule may require to be able to specify a condition. Define the
method leftPart:rightPart:condition: that returns a new rule and can be used as follow

Script 26.9

ParametricLSRule
leftPart: ’(A d)’
rightPart: ’(A d - 1)’
condition: [:each | each second > 0].
We would also like to be able to create an instance of parametric rule that does not require a condition
without having to specify the condition. Not specifying a condition should be equivalent to create a rule
with a condition always returning true as shown by the next script.

Script 26.10

ParametricLSRule
leftPart: ’(F d)’
rightPart: ’(F d * 1.33)’
should be equivalent to
ParametricLSRule
leftPart: ’(F d)’
rightPart: ’(F d * 1.33)’
condition: [:each | true]
Define the class method leftPart:rightPart: that returns a new well-formed instance of ParametricLSRule,
i.e., having a default block. Such a method can simply invoke the class method leftPart:rightPart:condition:
previously defined.
You could wonder why we create two different instance creation method while one is enough. We do
that for several reasons:

1. we want to avoid that user of the class have to define all the time the same condition.

2. By proposing to the user of the class ParametricLSRule different instance creation method we
want to avoid that someone could define a wrong block to represent the fact that a rule does not have
a condition.

In general, this is our responsibility as a the implementer of the class to provide methods that protects
the user to make mistake or to know too much of the underlying class structure or internal representation.
Now we are ready to define the class ParametricLSystem.

-2001 Stéphane Ducasse ([email protected])


254 Parametric L-Systems

4 The ParametricLSystem Class


As we described it in section 1, the derivation of a pLSystem is different than the one of a basic LSystem.
That’s why we define a subclass of the class LSystem named ParametricLSystem. The relatiobships
between the classes LSystem, LSystemSubFigures and ParametricLSystem is given by the
Figure 26.2: LSystemSubFigures and ParametricLSystem are both subclasses of LSystem,
this means that they are both reusing the behavior defined by LSystem and extending it.

Figure 26.2: Relationships between the different classes participating in the LSystem application.

Let us start by looking how we can evaluate the expression of a fragment. In the category ’computing’,
define the method evaluate: that given a fragment composed by a symbol and an expression, e.g., 2
* 3, returns a new fragment with the same symbol and the value of the expression, e.g., 6. The following
script shows some examples.

Script 26.11 (Illustration of evaluate:)

| lsys |
lsys := ParametricLSystem new.
lsys evaluate: #(A 2 * 3)
returns #(#A 6)
lsys evaluate: #(A 2 - 3)
returns #(#A -1)
lsys evaluate: #(A 2 + 3)
returns #(#A 5)

Hints. There are several ways to implement the method evaluate:. One interesting one is to use one
of the method perform defined on the class Object. For binary messages, perform:with: allows
one to say to an object to execute the method whose selector is given with argument the second argument.

Script 26.12 (Illustration of perform:with: use)

2 perform: #* with: 3
returns 6
4. The ParametricLSystem Class 255

Experiment 26.1
Enhance the method evaluate: so that expressions evaluated can be nested. You can set as constraint
that nested expression should always be binary operations, that they should not contain symbols like A or
F and that they should always be between parenthesis. For example, (A 2 * (3 * 4)) should return (A 24),
(A d * (3 * 4)) should return (A d * 12)

The next method we need is a method that given a sequence of elements (fragment or symbols), a
symbol representing a fragment argument and a its value returns a new sequence where all the occurences
of this symbol have been replaced by its value and where evaluable expressions are replaced by their
values. Let us call this method substitute:by:in:. The following script shows some examples of
substitute:by:in: use.
Script 26.13 (Illustration of substitute:by:in:)

| lsys |
lsys := ParametricLSystem new.
lsys substitute: #d by: 1 in: #((A d) d)
returns #(#(#A 1) 1)
lsys substitute: #d by: 1 in: #((A d * 2) d)
returns #(#(#A 2) 1)
lsys substitute: #d by: 1 in: #((A d) (A 0) (A 1 * 3) d)
returns #(#(#A 1) #(#A 0) #(#A 3) 1)
To implement substitute:by:in: we need some helper methods: First we need to implement a
method that returns true if an element of a rule is a fragment, false otherwise (Hint: you can check if the
class of the object passed as argument in from the class Array).
Script 26.14 (Illustration of isFragment:)

| lsys |
lsys := ParametricLSystem new.
lsys isFragment: #(A d)
returns true
lsys isFragment: #(A d * 3)
returns true
lsys isFragment: #[
returns false
lsys isFragment: #d
returns false
Second, we need to have a method that returns true if a fragment can be evaluated, i.e., with our
current implementation this means that the fragment is constitued by a symbol and an expression of the
form numberoperationnumber as shown by the following scripts.
Script 26.15

| lsys |
lsys := ParametricLSystem new.
lsys shouldBeEvaluated: #(#A 2 #* 3)
returns true
lsys shouldBeEvaluated: #(#A #d #* 3)
returns false
Once we have the two helper methods defined the method substitute:by:in: can be imple-
mented the following way. We iterate over the collection of production elements. For each element we
check if this is a fragment. If this is not a fragment we simply check if this is not the element that we
want to substitute. If this is the case we just return the value else the element because it does not have

-2001 Stéphane Ducasse ([email protected])


256 Parametric L-Systems

to be changed. In case the element is a fragment we substitute all the elements inside the fragment itself
and check wether the resulting fragment can be evaluated. If this is possible we evaluate it else we just
returning the new fragment whose symbols have been subsituted.
Method 26.2

substitute: element by: value in: aCollection

^ aCollection
collect: [:each |
(self isFragment: each)
ifTrue: [|res|
res := self substitute: element
by: value
in: each.
(self shouldBeEvaluated: res)
ifTrue: [self evaluate: res]
ifFalse: [res]]
ifFalse: [each = element
ifTrue: [value]
ifFalse: [each]]]

4.1 Reconsidering String as Collections of Characters

In Chapter 24 we chose to represent a rule as two strings one for the left part and one for the right part.
Some of you may have wonder why we did not use a character for the left part. Now it is time to explain this
choice! We made this choice because we knew that for parametric L-Systems we would have a composed
structure, like an array or a collection, for the left part and not a single element. As a string is a collection of
characters if we were able to implement something with strings we could reuse the methods for Parametric
L-Systems.
To illustrate this point, redefine the method treatSequence: of the class LSystem as follow and
check that all your previous L-Systems still work:
Method 26.3

LSystem>>treatSequence: aCollection
"Apply the rules to each element and return a string that
represents the result"

| col |
col := OrderedCollection new.
aCollection
do: [:anElement | col addAll: (self treatElement: anElement)].
^ col

This method creates an empty collection, an ordered collection, then iterates over the collection of
elements passed in argument: each element is treated (by invoking the method treatElement:) and
the returned result is added into the first created collection which is finally returned. What is important
to see is that the method treatElement: may return a right part of a rule, so it returns a collection of
elements as shown by the following Script 26.16.
4. The ParametricLSystem Class 257

Script 26.16 (Illustrating treatElement:)

| lsys rule1 |
lsys := ParametricLSystem new.
lsys axiom: ’(A 100)’.
lsys angle: 88.
rule1 := ParametricLSRule
leftPart: ’(A d)’
rightPart: ’(F d)[+(A d / 1.456)][-(A d / 1.456)]’.
lsys add: rule1.
lsys treatElement: #+
returns #(#+).
lsys treatElement: #(#F 2)
returns #(#(#F 2)).
lsys treatElement: #(#A 2)
returns #(#(#F 2) #[ #+ #(#A 1.373626373626374) #] #[ #- #(#A 1.373626373626374) #])
As show the following Script 26.17, the method treatSequence: should return a collection of
fragments and symbols and not a collection of collection of fragments and symbols
Script 26.17 (Illustration of treatSequence:.)

lsys treatSequence: #(#+).


returns anOrderedCollection(#+)
self treatSequence: #((#F 2)).
returns an OrderedCollection(#(#F 2))
lsys treatSequence: #(#+ #[ #(#F 2)).
anOrderedCollection(#+ #[ #(#F 2) #+)
lsys treatSequence: #(#+ #(#A 2) #-)).
anOrderedCollection(#+ #(#F 2) #[ #+ #(#A 1.373626373626374) #] #[ #- #(#A 1.37362637
What is really interesting with this method treatSequence defined on class LSystem is that it
works for simple L-Systems but also for Parametric L-Systems. This is due of the fact that a string is
a collection containing characters, String is a subclass of Collection. So any message sent to a
collection are acceptable by a string. In fact, we modified LSystem implementation after discovering that
pLSystem implementation could reuse it.

4.2 The Last Methods and You are Done!


Now the final method that is missing is the method treatElement:. This method is in fact doing the
most important work. Given an element of the production sequence it checks whether this element match
the current rules defined in the L-System, evaluates the expressions contained in the using the argument
values and return the production of the L-System. Define the method treatElement: and if you fail
read the following.
The method treatElement: should always returns a collection to satisfy the addAll: method
used on its result in the method treatSequence presented above. So the method treatSequence:
checks whether the passed element is a fragment, if this is not the case, this means that the element is
a symbol such as [, + so such a symbol is put inside an array and the method exits. When the element
is a fragment, the method checks whether there is rule matching this element. If there is no rule then a
collection having as single element the fragment is return. This is again done because we want to have a
fragment inside the production rule and returning simply a fragment would not work because addAll:
would add not a fragment but all its elements into the resulting sequence. If the fragment matches a rule,
the method then returns the right part of the matched rule where all the symbol have been replaced by
In Script 26.16, for example the fragment (A2) matches the rule (Ad) → (F d)[+(Ad/1.456)][−(Ad/1.456)]
therefore the result is to substitute d by 2 in the right part, to evaluate the expressions that can be evaluate
and return the new sequence of elements. Here the result is then (F 2)[+(A1.373626373626374)][−(A1.373626373626374)]

-2001 Stéphane Ducasse ([email protected])


258 Parametric L-Systems

Method 26.4

treatElement: anElement
"returns an array containing an Element if anElement is a single symbol
or if anElement is not a left part of a rule, else returns an array representing the
right part of a rule. Note that if this is a fragment it is evaluated"

| selectedRule |
(self isFragment:anElement)
ifFalse: [^ Array with: anElement].

selectedRule := rules detect: [:each | each doesMatch: anElement]


ifNone: [nil].
^selectedRule isNil
ifTrue: [Array with: anElement]
ifFalse: [self substitute: (selectedRule leftPart second)
by: (anElement second)
in: selectedRule rightPart]

A Powerful Turtle Now the only thing that we have to do is to change the way the turtle interpret the
information we give it. Indeed the turtle now has to know how to interpret elements (composed frag-
ments with values and simple symbols). We give you the following code, so that you can test faster your
implementation and have fun. The method interpretExpression: anExpression angle:
degree magnified: times interprets a single element.
Method 26.5

TurtleWithMemory>>interpretExpression: anExpression angle: degree magnified: times


| aCharacter |
(anExpression isKindOf: Array)
ifTrue: [aCharacter := anExpression first]
ifFalse: [aCharacter := anExpression].
aCharacter = #F
ifTrue: [self go: anExpression second * times]
ifFalse: [aCharacter = #+
ifTrue: [self turnLeft: degree]
ifFalse: [aCharacter = #-
ifTrue: [self turnRight: degree]
ifFalse: [aCharacter = #[
ifTrue: [self pushCurrentState]
ifFalse: [aCharacter = #]
ifTrue: [self restoreState]]]]

Method 26.6

TurtleWithMemory>>interpretExpression: aString angle: degree


self
interpretExpression: aString
angle: degree
magnified: 1
5. Fun with pL-Systems 259

Define the methods interpretExpressions: aCollection angle: degree magnified:


times and interpretExpressions: aCollection angle: degree that invokes the pre-
viously defined methods on collection of elements.
If you want to automate the sending of production results to a turtle, use the following method, or create
your own. With such a method you can now ask a pL-System to derive itself a number of times and to draw
itself.
Method 26.7

ParametricLSystem>>drawAtLevel: n magnified: times


| turtle derivation |
turtle := TurtleWithMemory new.
derivation := self deriveAxiomAtLevel: n.
turtle
interpretExpressions: derivation
angle: angle
magnified: times.
^ derivation

5 Fun with pL-Systems


Now we present you some interesting pL-Systems and discuss some differences in modelling.
Cactus (Figure 26.3)
Axiom A(200)
Angle 81 degree
Rule F(x) → F(x * 0.3) + F(x * 0.4582)- - F(x*0.4582) + F(x*0.7)
The values of the Cactus pL-System come from the fact that this L-System is based on the idea of
filling a square triangle. The expression we gave is based on the following one F(x)→ F(x*p)+F(x*h)–
F(x*h)+F(x*q) where p + q = hypotenuse, p is the distance on the hypotenuse from one corner to the
height of the triangle, q is the other distance on the hypotenuse from the other corner to the height of the
triangle, h is the height of the rectangle issued from the square angle. So if c is the hypotenuse length,

p = 0.3, q = 0.7 and h = p ∗ q.

Figure 26.3: Leaf 2, Level = 5,

Leaf 2 (Figure 26.4)


Axiom A(0)
Angle 45 degree
Rule A(d) → F(2)[+A(0)][-A(0)]F(2)A(0)
Rule F(d) → F(d * 1.456)

-2001 Stéphane Ducasse ([email protected])


260 Parametric L-Systems

Figure 26.4: Leaf 2, Level = 8, Magnified 1.2

L-Systems can create forms by appending segments of decreasing length to the structures obtained in
previous derivation steps like the following pL-System T Growing 1 does. Once a segment is treated, its
length does not change as shown by the top figure of the Figure ??.

T 1 (Figure 26.5)
Axiom A(1)
Angle 88 degree
Rule A(s) → F(s)[+A(s/1.456)][-A(s/1.456)]

A structure with identical proportions can be obtained by appending segments of constant length and
increasing the lengths of the previously created segments by a constant as shown by the bottom figure in
the Figure 26.5.

T 2(Figure ??
Axiom A(0)
Angle 88 degree
Rule A(d) → F(1)[+A(0)][-A(0)]
Rule F(d) → F(d * 1.456)

Figure 26.5: Two different ways of creating figures. The top figure is created by appending segments of
decreasing lengths. The bottom figure is created by increasing the length of previouslycreated segments.
6. Models of Compound Leaves 261

D R Derivation level
0 2.00 10
1 1.50 16
2 1.36 21
4 1.23 30

Table 26.1: Some D and R values for the symmetrical model.

6 Models of Compound Leaves


[?] shows how pL-Systems can model compound leaves. He introduces the following pL-Systems, one
representing symmetrical branches and the other one producing alternate branches. D represents the apical
delay, i.e., the time needed to branch and R the internode elongation rate. These two values are constants
that can be changed to produce different leaves. Try the values proposed in the tables 26.1 and 26.2
extracted from [?]. Pay attention that some of the results have to be minimized or maximized using the
method drawAtLevel:magnified: and that the parameters may have to be changed.

Figure 26.6:

Symmetrical Compound Leave (Figure 26.4)


Axiom A(0)
Angle 45 degree
Rule A(d): d > 0 → A(d - 1)
Rule A(d): d = 0 → F(1)[+A(D)][-A(D)]F(1)A(0)
Rule F(a): – → F(a * R)

Alternate Branching Plant (Figure ??)


Axiom A(0)
Angle 45 degree
Rule A(d): d > 0 → A(d - 1)
Rule A(d): d = 0 → F(1)[+A(D)]F(1)B(0)

-2001 Stéphane Ducasse ([email protected])


262 Parametric L-Systems

D R Derivation level
1 1.36 20
4 1.18 34

Table 26.2: Some D and R values for the symmetrical model.

Rule B(d): d > 0 → B(d - 1)


Rule B(d): d = 0 → F(1)[-B(D)]F(1)B(0)
Rule F(a): – → F(a * R)

Try to produce your own pL-Systems, for example, you can modify the delay of the rule selection by
changing the value of the argument passed.

7 Enhancements
◦ Rule printing
◦ ensuring that the right class is used for rules.
◦ Introduce a way to manage constants: For example using collection of pairs and substituting the value
before the rewritting.
◦ The computation of the value can be radically speed up by caching the results and checking if they
have not already been computed before computing them. This is especially relevant in our case where
the computation uses always the same number.
◦ introduce the possibility to handle multiple parameters

Experiment 26.2
Use parametric L-System in the L-Systems modeling plant. You can for example control in a finer manner
how the branches should grow.
Part V

The Squeak Object model

-2001 Stéphane Ducasse ([email protected])


Part VI

Other approaches for teaching

-2001 Stéphane Ducasse ([email protected])

You might also like