OOP MacForth
Volume Number: 3
Issue Number: 2
Column Tag: OOPS in Forth
Object Oriented Programming for MacForth
By Paul Snively, Contributing Editor, ICOM Simulations, Inc.
OOPS.
They're getting to be all the rage.
The Macintosh design was based on one. There are several available for a wide
variety of hardware. Some are OOPS through and through, others are just OO
extensions to existing languages.
Welcome to OOPS, the column dedicated to Object-Oriented Programming Systems
for the Macintosh .
It's hoped this column will evolve into a comprehensive look at object-oriented
programming on the Macintosh. For now I will focus my efforts on two languages, one
of which has had a vast impact on people's understanding of object-oriented
programming and supports a tool that makes standard Mac user-interface support
much easier. The other is only of interest to die-hard FORTH programming freaks like
myself.
The first language is Object Pascal, and the second can be either Neon or
ForthTalk. ForthTalk is a new product which I just received, and I already like it
much more than I like Neon. We'll get into Object Pascal, either TML or MPW, next
time.
ForthTalk
ForthTalk is a $55 extension to the MacFORTH programming language that adds
capabilities to the system generally described as "flavors," a term I believe, if
memory serves correctly, was borrowed from Common Lisp. (Incidentally, one of my
pet peeves is that even object-oriented programmers don't seem able to agree on a
standard terminology to describe their system's capabilities. I want to know exactly
what is wrong with Smalltalk's terminology of Classes, Subclasses, Superclasses,
Instances, and Methods. One answer, that few object-oriented languages offer multiple
inheritance, and "flavors" provides an intuitive framework in which to discuss that
capability, will be discussed in a moment).
ForthTalk is distributed on two disks. The first disk contains the tools to create
the ForthTalk Kernel. These include the "Token Resizer" (for expanding the number of
definitions that MacFORTH can handle) and the "ForthTalk Loader", along with a
slightly reworked copy of the "FORTH Blocks" file and a copy of the standard MacFORTH
block file editor. The second disk is the library disk. It contains the source code to the
"Vanilla" flavor (analogous to Smalltalk's "Object" class) plus a few more useful
flavors, source code to the tutorial in "Chapter 1," plus source code files to a couple of
demos that aren't described step-by-step within the documentation.
ForthTalk is an extension to the current version of the MacFORTH kernel ( which,
as of this writing, is K2.4). This means you must be a licensee of MacFORTH K2.4
from CSI in order for ForthTalk to be of any use. Unfortunately ForthTalk does not
appear to work with the Level 3 "Developer's version" of MacFORTH; working only
with Level 2. Support is planned for CSI's new MacFORTH PLUS kernel.
Getting ForthTalk up and running is very simple. The author, Steve Lindner,
apparently decided that MacFORTH's default limit of 500 tokens per program was not
enough, so he provided a utility that allows MacFORTH programmers to use more
tokens, the recommended number being 2000. This application is executed first to
make room for the extensions, leaving some for user-written programs.
The ForthTalk Loader is then executed. This interesting piece of code is
essentially a customized version of CSI's vocabulary librarian, which is included with
the Level 2 MacFORTH system. Written by Ward McFarland, the FORTH code grabs the
precompiled extensions from a couple of resources in the resource fork, neatly
sidestepping the issue of how to distribute ForthTalk without distributing either the
MacFORTH kernel or the source code to ForthTalk, which might prove damaging to
InSite Computing. The ForthTalk Loader, having read in the extensions and attached
them to the MacFORTH kernel, snapshots the kernel so that the extensions are a
permanent addition to the kernel. The system then returns to the Finder.
That's all it takes to make a ForthTalk system. The remaining magic lies in the
"Vanilla" file.
Vanilla contains the definitions of several useful flavors. The most obvious of
these is Vanilla itself, the root flavor. Every other flavor is a subflavor of Vanilla.
(Almost every other flavor. The exceptions are called mixins, and I'll get to them in a
minute).
You're probably wondering "If this is the root flavor, why doesn't it get
snapshotted into the kernel along with the flavor defining words?" The reason why is
one of the few shortcomings of an otherwise fine system. In the current version of
ForthTalk, flavors cannot be correctly snapshotted because they, or more accurately,
their instances, exist as entities in the application heap, not in the FORTH dictionary.
Furthermore, in many cases, the instance may point to an instance of another flavor,
etc. Ultimately a snapshot utility will be written to deal with these unique
requirements.
Having created the ForthTalk kernel, how is it used? The easiest thing to do first
is simply double-click on the "Vanilla" file from the Finder. This will launch the
ForthTalk kernel, load the "FORTH Blocks" file, and in turn the editor, and ultimately
the "Vanilla" file.
You are now in a powerful object-oriented FORTH environment. The best thing to
do now is to read Chapter One of the documentation. It is a simple, fun tutorial which,
although simple in nature and objectives, covers everything from defining new flavors
and methods to the important concept which sets this system apart from all others I
have used: multiple inheritance.
A really basic data structure is always nice. So let's define an array, like this:
FLAVOR Array Vanilla |
Note: in ForthTalk, uppercase and lowercase are distinct. Make sure you case
things consistently. In general, ForthTalk uses the convention that all uppercase
words are "built-in" to the system, whereas mixed-case words were defined outside
the kernel.
What have we done with the above line? We have created a new flavor called
Array. It has Vanilla as a superflavor. The vertical line indicates that we are done
defining the new flavor. Experience OOPS users are probably scratching their heads,
wondering why you must explicitly tell the compiler when you are finished defining a
new flavor. The answer is that with multiple inheritance, you can have more than one
superflavor.
We've now defined a flavor called Array. What does it do? Nothing at this point.
We need to tell it what kind of data structures and what kind of code make up an Array.
The tutorial opts to keep things simple and use a fixed-length array. In order not
to distract from our discussion of object-oriented programming, I will do the same.
Let's make arrays to be big enough to contain ten longword (32-bit) values.
We'll also need a way to keep track of how many of those ten elements are occupied.
Most OOPS call the data structures within their classes (or flavors, in
ForthTalk) "instance variables" because they are unique for all instances of that class.
ForthTalk is really no different, but it is different from most OOPS in not using
instances of other flavors as instance variables. Instead it relies on "INST.XXXX
words ("INST.LONG," "INST.WORD," and "INST.SPACE") to create instance variables.
So we can give Array its data structures like this:
INST.LONG Array Count
40 INST.SPACE Array Space
We have just allocated a total of 44 bytes (40 bytes plus a longword) to Array. 4
bytes can be referred to as "Count" in the methods that we write for arrays, and the
remaining 40 are referred to as "Space.
Believe it or not, we have now given the flavor "Array" enough to work with in
order to create arrays! We can create an object whose flavor is "Array" like this:
Array CONSTANT MyArray
As you can see from this example, flavors are fairly intelligent FORTH words.
When you execute a flavor, it figures out the total amount of space that it must allocate
(44 bytes in Array's case), and allocates a block of that size in the heap. NOTE: flavors
return a HANDLE to their data!
Since flavors return a handle, we can simply use the normal FORTH word
CONSTANT to assign a name to the handle.
We now have a new flavor that we've created, and we've used it to create an
instance of itself, which we've named MyArray. What can we do with MyArray?
At this moment MyArray is doing nothing but taking up space. What we need are
methods for operating on the data in MyArray, and on any other arrays that we create.
One obvious thing to do would be to store things in our array. We want to create a
method to do this, so let's call ours :Set. In ForthTalk, all methods begin with a colon
(":"), which the documentation says is a convention used by Smalltalk. I beg to differ
with the documentation on this point; in Smalltalk, methods end with a colon. For the
sake of internal consistency I have chosen to use the ForthTalk convention.
The definition of :Set looks like this:
METHOD Array :Set ( x n ---)
4* Space + ! ;M
This method takes two operands on the stack. The value to store is first, and the
element within the array is on the top of the stack.
The method first multiplies the array index by four since each entry is four
bytes long. It adds the result to Space (which refers to the first byte of the array).
The result is the address where we wish to store our value, and the normal FORTH