ForthOS Object Oriented Extensions
Object oriented systems often offer dynamic collection of unused memory ("garbage collection"); ForthOS, like C++, but unlike Smalltalk and Python, does not offer garbage collection.
only also extensions
A "class" is the name for the shape of a particular data structure, along with the functions it provides for manipulating that data structure. For the predefined classes supplied with ForthOS, these classes each have a name, which is registered in the "extensions" vocabulary. In this section, we will illustruate the OO facilities using a Set, which is a data structure which can hold an arbitrary collection of numbers; if a number is added more than once, it exists in the Set only once. There are methods for adding and removing members, as well as for testing for the presence of members.
First we will create a new Set for us to play with:
Set -> new constant mySet
Now we can see what's in our new Set:
mySet -> .
As you can see, a new Set is empty. Let us add some elements, and then print its contents (actually, ask the data structure to list its own contents):
1 mySet -> add 123 mySet -> add 1 mySet -> add
Arguments are passed on the Forth operand stack just like they would be for any other word. The difference is that instead of "add" having a single unique set of actions, "-> add" causes the pointer on the top of the stack (which points to our Set instance--we access it via "mySet") to be used to look up its type (Set), and then find the code for "add"'ing to a Set, and then jumping into that Set-specific code.
Now we verify that the Set holds both 1 and 123 (but 1 only once, even though it was add'ed twice):
mySet -> .
As you might expect, it's easy enough to remove something from a Set:
1 mySet -> remove mySet -> .
Set -> :method addRange ( high low self -- ) -rot do i over -> add loop drop method;
First, notice that the word "Set" in the "extensions" dictionary is actually just a Forth constant which pushes a pointer to an object onto the stack. What object? The object which describes the Set class! So then when this object is told "-> :method", it tells the ForthOS OO system to define a new method named "addRange" for the Set data structure.
Next, notice that the actual code defined receives, in addition to the user provided arguments, a pointer to the Set instance on which "addRange" was invoked (our stack comment, in the tradition of Smalltalk, calls this "self"). If we invoke this new addRange on our Set instance mySet, "self" would be the same value as if we simply executed "mySet .".
The actual code body of a method shows that OO compilation is a seamless blend of traditional Forth and any needed OO invocations. We use "-rot do" to start a loop across the requested values, "i over" to get the current iteration value, along with a copy of the pointer for our Set instance. Then we use "-> add" to add the value of "i" to the contents of this Set.
Definitions are finished with "method;" rather than just ";". There is some unique cleanup needed when building an OO definition. If you forget, the compiler will flag the ";" as an error.
One way would be to create a BigSet class. It's just like a Set, but it elides the actual list of members. First we define a new class, inheriting from Set:
Set -> subclass: BigSet
Now we redefine just the printing method:
BigSet -> :method . ( self -- ) drop ." a BigSet{...}" method;
You can use a BigSet just like a Set, because it uses the exact same data structure design as a Set, and offers all the same methods:
BigSet -> new constant bset1 123 bset1 -> add 456 bset1 -> add 789 bset1 -> add 456 bset1 -> remove bset1 -> .
BigSet -> :method .elems ( self -- ) -> size 0> if ." ..." then method;
This avoids a subtle bug in our simpler definition for "."; if we further subclassed BigSet as, say, BiggerSet, the "." method would continue to incorrectly describe the data structure as a BigSet. The generic method leveraged from Collection gets the correct name.
This definition also avoids printing ellipses when the BigSet is, in fact, empty. We could even have BigSet list its members until the size gets beyond 20 members:
20 constant ellideSize BigSet -> :method .elems ( self -- ) -> size ellideSize < if super-> .elems else ." ..." then method;
This version uses a new construct, "super->". Rather than invoke the method defined for this particular object, it invokes the method which would have been used if our current object was actually an instance of our parent class. So in this case, the parent of BigSet is Set, and "super-> .elems" will execute the ".elems" code for a Set.
For BigSet's which have not grown large, we still see their contents (empty BitSet's are also displayed correctly by Set). Once the size reaches ellideSize, we instead display ellipses.