|This page has links to outside of the Scratch website or Wikipedia. Remember to stay safe when using the internet as we can't guarantee the safety of other sites.|
|This article or section documents a feature not included in the current version of Scratch (2.0). It is only useful from a historical perspective.|
Smalltalk had been used in Scratch since the first version of Scratch was released in 2007 until the release of Scratch 2.0 in 2013. Using a Squeak virtual-machine (VM) by MIT slightly edited to meet Scratch's needs, Smalltalk is the coding language that creates the Scratch world, blocks, sprites, and everything the Scratcher sees when they use the application. Recently there has been an influx of users who want to get into the Scratch browser and tinker with the source code, and a whole host of Scratch Modifications have sprung up as a result. These modifications sometimes allow users to edit Smalltalk as part of their features, such as Panther, where users can create their own blocks using a Smalltalk window. With this sudden craze of modifying Scratch, a great many guides and questions have been posted all over the Advanced Topics teaching various aspects of editing Scratch and using Smalltalk. This Smalltalk "library" allows all the creators of these guides to pool their resources and compile a large collection of easy to understand tutorials in using Smalltalk within the Scratch VM.
|Note:||In this tutorial, the format multipleWordsWithoutSpaces is used a lot. This is standard programming practice to represent many words without spaces, and is called camel case.|
- 1 How a Scratch block comes to be
- 2 Getting to the blocks' display
- 3 Arguments
- 4 Block shapes
- 5 Methods
- 6 Temporary Variables
- 7 Strings
- 8 Methods Which Report Values
- 9 Conditional Code
- 10 Classes and Instances
- 11 Syntax Errors
- 12 External Links
- 13 References
How a Scratch block comes to be
A good way to introduce Squeak to a Scratcher is to explain how to make blocks. A block consists of two parts: the blockspec and the method. A blockspec defines the block: it tells Scratch what the block is and does. The method explains to Scratch how to do what it does. It is similar to a table of contents. The blockspec is what Scratch looks up, and the method is the page it finds, which it reads.
The blockspec itself consists of three parts: the title, which is the text displayed on the block, the type, which is the shape of the block (reporter, predicate, cap, stack, c-shaped, timed, or special form), and the method selector, which is like the page number: it tells Scratch which code the block should evaluate.
The title is a formatted string, which is a string of a particular format. For example, the strings "Hi56", "Bye813", and "Pi_is_3.14159" all follow the pattern Text+Number. In a block, the formats describe the inputs of our blocks, called arguments. For example, "%s %n" describes a block with a String input (text box) and a numerical input (text box that accepts just numbers, decimal points, and the negative sign (-)). Arguments are covered in detail in the following section.
Getting to the blocks' display
To get to the blocks' display, or blockSpecs, first shift-click the R in the Scratch Logo in Scratch 1.4 and Turn fill screen off. Then click in the white space and on the menu that pops up select Open. Then select Browser on the next menu that pops up. A giant yellow box should pop up. On the list of things in the top left corner of the box, select Scratch-Objects which should be first in the list. Then select either ScratchSpriteMorph or ScratchStageMorph in the list centered-left at the top of the box. Now below that list, click Class. Then select block specs, the only one in the list centered-right, and then select blockSpecs in the rightmost list.
In Scratch, blockspec titles are formatted with %a, %b, etc. for arguments. However, in a popular modification Panther, a title is formatted with $something$. This allows any number of characters in the formatted argument (note how since Scratch needs %+a symbol, only 26*2=52 types of arguments can be made). Here's a list of commonly used arguments that can be put in a block followed by the format code for Scratch and Panther and a description:
|Argument||Scratch symbol||Panther symbol||Description|
|String||%s||$String$||Accepts any characters|
|Number||%n||$Number$||Accepts numbers, periods, commas, and the negative sign (-)|
|Boolean||%b||$Boolean$||Accepts boolean (diamond) blocks|
|Sprite||%m||$Sprite$||A dropdown with a list of sprites|
|Variable||%v||$Variable$||A dropdown with a list of variables|
|Attributes||%a||$Attributes$||A dropdown with a list of attributes (direction etc.)|
|Color picker||%C||$Color$||A color picker with a popout pallet|
|Color picker||%c||$ScreenColor$||A color picker without a popout pallet|
|Directions||%d||$Directions$||A dropdown with a list of directions|
|Drums||%D||$Drums$||A dropdown with a list of drum sounds|
|Events||%e||$Event$||A dropdown with a list of broadcasts|
|Math functions||%f||$MathFunctions$||A dropdown with a list of math functions (abs, sin etc.)|
|Effects||%g||$Effects$||A dropdown list of effects such as color and brightness|
|Sensors||%h||$SensorNames$||A dropdown list of sensor names such as slider (for a PicoBoard)|
|Sensor booleans||%H||$SensorBooleans$||A dropdown list of PicoBoard booleans such as "A connected?"|
|Instruments||%I||$Instruments$||A dropdown list of instruments|
|List options||%i||$ListIndex$||A dropdown of list indexes (1, last, any)|
|Key options||%K||$Keys$||A dropdown list of keyboard keys|
|Lists||%L||$List$||A dropdown list of lists|
|Costumes||%l||$Costumes$||A dropdown list of costumes|
|Motors||%W||$MotorNames$||A dropdown list for the Motor Blocks|
|Notes||%N||$Notes$||A note space with popout keyboard|
|Sounds||%S||$SoundNames$||A dropdown list of sounds|
The next part of a blockspec defines the block shape. This is added with a
# symbol. Here are the shapes:
|g||Color (Panther (Scratch Modification) only)|
|t||Timed block||These blocks have the authorization to pause a script for some time, evaluating a command. Examples: Wait () secs, Play note () for () beats|
|s||Special form||For example, if/else has two "c" inputs. These need special code.|
A method is a function, such as "Make a cup of tea". A method can have arguments, such as "Make _ cups of tea". They can also return a value, such as "Amount of sugar in _ cups of tea". A Squeak application runs on basically a collection of methods.
In Squeak, a method runs on three parts: the method selector, the temporary definitions, and the code. It looks like this:
aMethod | aTemp | someCode
Before continuing, it is important to note that in Squeak, a comment is enclosed in double quotes ("). Comments are unevaluated code which tell us what is happening, for example:
self beep "make some noise!"
It is considered good programming practice to give an explanation of the method (and credits, etc.) in a comment right below the method selector.
Methods can have arguments. In Squeak, an argument is provided in a method with the syntax
Methodname:argument moreOfName:moreArguments. So Make _ cups of tea looks like this:
|Note:||You cannot have method names after the last argument, so |
Temporary variables are called "temps". They are variables the method can use within itself but are not visible or usable anywhere else.
Temporary variables are listed below the method selector (see previous section) between two pipes (|). They are restricted to alphabets and numbers, and cannot be reserved (i.e., self).
It is conventional to name them t1, t2, t3, etc., but not necessary; in fact it is better to name them something legible. However, the Squeak compiler always converts temporary and input variables (arguments) to t1, t2 format.
A variable is modified with the syntax
variable _ value.
|Note:||In the Squeak editor, the underscore is depicted as an arrow facing left (←).|
modifyAVariable | aVar | aVar _ 10
Strings are data; pieces of text which are not evaluated, but reported as they are. In Squeak, strings are represented within single quotes, examples:
To represent a single quote within a string, use two single quotes in a row:
'my clock''s ticking'.
To concatenate (join) strings, enumerate them separated by commas:
'Hello, ', 'John, ', ' what''s ', 'up?' "Hello John, what's up"
Note that a variable can be concatenated to a string:
t1, '= 5'.
Methods Which Report Values
A method can report a value, as noted above. In Squeak, this is done with the syntax
|Note:||In the MIT Squeak editor, ^ becomes an arrow facing up (↑)|
report:aValue ^ aValue
Square brackets are used for conditional code. Conditional code is similar to the hole in a C-shaped Scratch block like the If () Then block — it may or may not be evaluated, or may be run many times. It is just code which the program can do anything with.
Conditional code is written like normal code, but in square brackets:
[t1 _ 33
^ 'hey, there']
In Squeak, there are many uses of conditional code. Here are some:
| t1 t2 | t1 _ (1 = 0) "t1 contains the value true" t2 _ (1 = 1) "t2 contains the value false" t1 ifTrue: [^1]. "report 1 if t1 is true" t1 ifFalse: [^2]. "report 2 if t1 is false" t1 ifNotNil: [^3]. "report 3 if t1 is not nothing" 3 timesRepeat: [t1 _ t2]. "set t1 to t2 three times"
Classes and Instances
A class is a collection of
- Attributes or properties (pieces of data unique to each class)
|Note:||With the abstraction of first-class data, a class method can be thought of as an attribute set to a lambda (unnamed function).|
There are two kinds of classes: static classes and dynamic classes. A static class is just a collection of methods which can be run.
A dynamic class is more interesting. A dynamic class represents an object. For example, the class "car" could represent a car. You can then create instances of these dynamic classes and set them to variables. For example, you can set "Mini Cooper" to a "car".
Dynamic classes include methods, so you can control them, for example, you can send the "car" object a "drive" method. They also have properties or attributes, which are pieces of data unique to the class. For example, the "car" class has the "color" attribute. Of course, attributes can be classes in themselves. For example, a "Mini Cooper" class can have a "car" class as an attribute.
However, an attribute of a class "x" cannot be an instance of the class "x", because the compiler will have to go in an infinite loop to keep initialize the classes.
Another important thing about dynamic classes is the copying protocol. When you set a variable to an instance of dynamic class, you do not create a copy of the dynamic class. You create a reference to the class, which when edited (say an attribute incremented), also affects the original instance. Thus, classes should provide a copying protocol, a method which returns a copy of itself by actually assembling the copy attribute by attribute. A copying protocol example will be given in a following section.
Every class inherits from another. An inherited class, called a
subclass is given all the class variables and methods of the parent class. For example, if "Mini Cooper" inherits from "Car", the "Car" class can define the "Color" attribute, and all Cars will not need to define their colors separately.
The class which inherits (
Mini Cooper) is called the Child or Subclass of the other class (
Car), which is called the Parent or Superclass.
Morphs inherit from the
Morph parent class.
Morph inherits from Squeak's
Object is the root class. All high-level languages provide a root class which is blank, with only atoms as attributes. All classes are (directly or indirectly) subclasses of the Root Class. For example, in Objective-C, the root class is
NSObject. All other classes must inherit from either
NSObject or a class which inherits from
NSString inherits from
NSAttributedString inherits from
NSString. The inheritance is depicted with colons. Inheritance is shown right to left:
NSAttributedString : NSString : NSObject NSOpenGLView : NSView : NSObject
Imagine the following inheritance model:
Mini Cooper : Car : Vehicle : Object
Car? Is it a subclass of
Vehicle or the parent of
The answer is, both. In other words, every class can be visualized as its parent's child or its children's parent. This model of inheritance is known as Prototyping. In BYOB, the sprite-object-model is based on prototyping.
In Squeak, a class coding is extremely similar to natural language. In fact, Squeak is known as the most natural programming language.
Creating a Dynamic Class
First, open up the System Browser. Above the scroll bar in the list on the left side, click on the button with the dash (-) on it. Then select "add item...". When prompted, type in the name of the category to which your class belongs. A category can have many classes, like vehicles can have car and helicopter. For this tutorial, name it "Vehicles".
In the editing pane, you should have this code:
Object subclass: #NameOfClass instanceVariableNames: 'instVarName1 instVarName2' classVariableNames: 'ClassVarName1 ClassVarName2' poolDictionaries: '' category: 'vehicles'
Replace it with this. Read the annotations carefully.
Object subclass: #Car "Class name" instanceVariableNames: 'driver' 'color' "attributes" classVariableNames: '' "ignore for now" poolDictionaries: '' "ignore for now" category: 'vehicles' "category name"
You have created the class! The class name is "Car", the attributes (Instance variables) are "driver" and "color", and category is "vehicles".
Now alt-click on the pane and select "accept". If prompted, type in your initials.
Next, we will add a simple method. Click on "Car" in the list second from the left in the System Browser. In the next list, click—all--. In the editing pane, you will get a sample method:
message selector and argument names "comment stating purpose of message" | temporary variable names | statements
Highlight all of it, and replace it with this:
drive Transcript show: 'Vroom!'
Again, alt-click and select accept.
Now open the World Menu. Select open... >> Transcript. The transcript is a log where programs in Squeak can log values for debugging purposes.
Also open up a Workspace. A Workspace is a simple terminal to evaluate Squeak code. Type in the following code to test your now class:
t1 := Car new. "Set t1 to a new Car" t1 drive "Drive the new car" "This code will be explained in the next section"
Alt-click and select "do it."
In the transcript, you should see the text "Vroom!". This indicates the car was successfully created.
Using a Dynamic Class
Instances are created with the "new" command:
myFamilyCar := Car new.
Class methods or attributes are called with the syntax:
class method class attribute
myFamilyCar color _ 'black' myFamilyCar driver _ 'Dad' carcolor _ myCar color myFamilyCar drive
In Squeak, all classes are provided with a special constant called
self, which a reference to itself. If you, for example, need a method in the "Car" class which sets its color, you would reference the self object:
setMyColor:aColor self color _ aColor
A copying protocol can then be written as follows:
copy | myCopy | myCopy := Car new. myCopy color _ self color myCopy driver _ self driver ^ myCopy
Now to copy a car, you can just say
myFamilyCarCopy _ myFamilyCar copy
Morphic classes define Morphs. To create a Morph, a subclass of
Morph, rather than the root class
Object is used. Here is the class definition (create a category called
Morph subclass: #MyMorph instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'My-Morphs'
This morph can already be drawn, with the following code (in a Workspace):
MyMorph new openInWorld.
This renders a blank blue rectangle.
A more interesting Morph can be created by implementing the
drawOn: aCanvas | acolor | acolor := Color random. "get a random color" aCanvas fillOval: (self bounds) color: acolor "draw something"
Morphs can react to events, such as clicks. Two methods are required: one to listen for clicks and one to act on the event:
handlesMouseDown: evt ^ true "listen"
mouseDown: evt self position: self position + (0@10)
Morphs can also be animated. Here, three methods are required:
startAnimation "initialization" self startStepping
stepTime "speed, in milliseconds" ^75
step "how to move" self position: self position + (1@1)
Of course, to use a morph efficiently, it must be assigned to a variable, or it will not be editable. To assign a Morph to a variable, the variable is assigned its initialization:
t1 := aMorph new openInWorld.