|
3.
What Makes Pocket Smalltalk Different
Pocket Smalltalk is in
some ways a very different kind of Smalltalk system than what you
may be used to. Many of these differences stem from three important
characteristics of this system:
- It is a cross-compiler,
that is, it creates executable programs which run on the PalmPilot
rather than on the host PC.
- It is designed to
be compact. Some changes from traditional Smalltalk have
been made to reduce memory consumption.
- It is declarative.
Programs can be represented in a linear form free of "floating"
references.
Declarative Smalltalk
Pocket Smalltalk is an
example of a declarative Smalltalk system. The new ANSI Smalltalk
standard is moving toward declarative systems. Whereas a traditional
Smalltalk program is built by extending the base image, in the process
building arbitrary graphs of objects, a Pocket Smalltalk program
is completely defined by its classes and methods. As a consequence,
a Pocket Smalltalk program can be represented as a simple file in
"file-out" format. In contrast, a traditional Smalltalk program
cannot be represented declaratively at all, and can only be recreated
by reapplying the series of actions used to extend the base image.
Pocket Smalltalk uses
a simple and flexible source code management facility called "packages".
A package is simply a file in standard Smalltalk file-out format.
The IDE knows about packages and provides a user interface for partitioning
your classes and methods between packages. In this way, you can
separate base system code from your own application code. Packages
are described in more detail in later chapters.
Starting Up
the #start method
When your compiled program
first starts up, it begins by sending the selector #basicStart to
the class Smalltalk, which by default resends #start. You must replace
the default #start method with your own in order to get your program
running. This is analogous to the "main" routine in C, and is somewhat
unique to this version of Smalltalk. Most Smalltalk systems do not
have any concept of a well-known entry point, but Pocket Smalltalk
programs always begin this way.
Symbols
Symbols are used for
three main purposes in Smalltalk:
- Naming methods (i.e.,
selectors)
- Naming classes
- Serving as unique
"tokens"
A symbol is written
as #symbolName, where symbolName is any valid Smalltalk identifier.
Since symbol data accounts
for a large fraction of the total size of a typical Smalltalk program,
Pocket Smalltalk will replace each symbol by a unique SmallInteger
at runtime. As a consequence, there is no Symbol class, nor is there
a way to distinguish between symbols and integers at runtime. This
is almost never a problem in practice though, and if necessary,
symbols can be "wrapped" in another class to provide the desired
functionality.
This optimization saves
a considerable amount of memory space and execution time. The only
real disadvantage, aside from losing the distinction between Symbols
and SmallIntegers, is that you will be unable to access the characters
of a symbol (for example, in order to print the symbol). Again,
this should not be a problem since Symbols should not be printed
at runtime anyways (Strings should be used for this purpose).
For debugging purposes,
you may choose to include the text of each symbol (select the "emit
debug info" option in the IDE). Then you may use the expression:
Context textOfSymbol: symbol
to recover the String
containing the symbol's characters. The default MiniDebugger uses
this feature to provide a symbolic stack trace when an error occurs.
The System Dictionary
In Pocket Smalltalk,
the system dictionary called "Smalltalk" is actually a class. You
cannot look up classes dynamically (using #at:, for instance) at
runtime as you can with other Smalltalk systems. You also cannot
add or remove classes (or methods) at runtime.
Integers
Pocket Smalltalk currently
does not provide LargeInteger (arbitrary size integer) arithmetic,
due to the large amount of memory space required by such a facility.
This may be added later as an add-on package. For now, integers
of magnitude -2^31 to 2^31-1 can be represented in Pocket Smalltalk
(i.e. 32 bit signed integers). Integers between -16384 and 16383
are represented directly as SmallIntegers and take no object storage.
Integers with larger magnitudes are represented as instances of
LongInteger allocated in the heap. Conversions between the integer
types occur automatically.
Proxies, nil, and doesNotUnderstand:
When an object is sent
a message which is cannot interpret, a Message object representing
the message send is created, and then the message #doesNotUnderstand:
is sent to the object with the Message as the argument.
By default, #doesNotUnderstand:
just signals an error, but certain classes may want to intercept
#doesNotUnderstand: and take some special action.
A class which provides
very little behavior of its own, but instead forwards most messages
onto another object is called a "proxy". Proxies and other classes
with similar needs can subclass from nil instead of from Object
(or a subclass of Object), thereby inheriting none of the "basic"
methods defined by class Object. They may then use #doesNotUnderstand:
to forward messages to another object.
A class inheriting from
nil need only implement the one selector: #doesNotUnderstand:. The
cross compiler does not allow you to create a subclass of nil which
does not implement this selector.
#become:
The primitive operation
Object>>#become: swaps the identity of two objects. In Pocket Smalltalk
this is an efficient operation. You must be careful with this operation
because it can crash the virtual machine if you "become" the receiver
of a method into an object with fewer named instances variables.
The "become" primitive will fail if you try to convert from a pointerless
object (e.g., a string or a byte array) to a pointer object, or
vice versa. It will also fail if you try to swap the identities
of SmallIntegers, or if either object is a statically allocated
object (i.e., a class, metaclass, or literal).
The built-in collection
classes implement expansion by means of #become:. This allows a
collection to be represented by a single object, rather than two
objects as in some Smalltalk implementations.
Global variables
Global variables are
handled a bit differently in Pocket Smalltalk than in other Smalltalk
systems. Rather than being Associations in the Smalltalk system
dictionary, global variables are class variables of Object. Since
all classes (except root classes; see above) are subclasses of Object,
they all can access these class variables the same way as global
variables.
At compile time, references
to class (and "global") variables are converted into single instructions.
No separate objects are created for global variables; nor can you
set the value of a global variable at compile time (it must be done
with some kind of initialization code at runtime).
Unlike other Smalltalks,
classes are not global variables. For most purposes, you can treat
them as such, but the major difference is that you cannot assign
to them. Class names must not conflict with class variable names.
#ifNil:
A very common idiom in
Smalltalk is the following:
object isNil ifTrue: [...]
This expression can be
simplified by defining a new message #ifNil: so that you can write:
object ifNil: [...]
The problem with doing
this is that ordinary Smalltalks cannot apply compiler optimizations
to this expression, so the above code will execute more slowly and
take more space than the usual isNil ifTrue: case. Pocket
Smalltalk, however, knows how to optimize ifNil: and related
messages, so you can use them without any penalty. The message forms
it recognizes are as follows:
- ifNil: [...]
- ifNotNil: [...]
- ifNil: [...] ifNotNil:
[...]
- ifNotNil: [...] ifNil:
[...]
- orIfNil: [...]
The last message deserves
some explanation. orIfNil: answers the receiver if the receiver
is not nil, but if the receiver is nil it answers the result of evaluating
the argument block. This can be used to provide "default" values for
possibly-nil variables:
^name orIfNil: ['anonymous']
Next: Interfacing
with the PalmOS
|