Forth Inspired Command Language | |
Author: John Sadler (john_sadler@alum.mit.edu) | |
This file created: 6 June 2000 | |
Revised: |
Object Oriented Programming in ficlReview of OO ideasClick here for a short review of OO ideas and implementations in other languagesDesign goals of Ficl OO syntaxFicl's object extensions provide the traditional OO benefits of associating data with the code that manipulates it, and reuse through single inheritance. Ficl also has some unusual capabilities that support interoperation with systems written in C.
AcknowledgementsFicl is not the first Forth to include Object Oriented extensions. Ficl's OO syntax owes a debt to the work of John Hayes and Dick Pountain, among others. OO Ficl is different from other OO Forths in a few ways, though (some things never change). First, unlike several implementations, the syntax is documented (below) beyond the source code. In Ficl's spirit of working with C code, the OO syntax provides means to adapt existing data structures. I've tried to make Ficl's OO model simple and safe by unifying classes and objects, providing late binding by default, and separating namespaces so that methods and regular Forth words are not easily confused. |
Ficl Object ModelAll classes in Ficl are derived from the common base class OBJECT. All classes are instances of METACLASS. This means that classes are objects, too. METACLASS implements the methods for messages sent to classes. Class methods create instances and subclasses, and give information about the class. Classes have exactly three elements:
Note for the curious: METACLASS behaves like a class - it responds
to class messages and has the same properties as any other class. If you
want to twist your brain in knots, you can think of METACLASS
as an instance of itself.
|
Tutorial (finally!)Since this is a tutorial, I'm assuming you're following along by pasting the examples into ficlWin, the Win32 version of Ficl (or some other build that includes the OO part of softcore.c). I also assume that you're familiar with Forth. If not, please see one of the references, below. Ficl's OOP words are in vocabulary OOP. To put OOP in the search order and make it the compilation wordlist from the default search order (as set by ONLY), type:ONLY ( reset to default search order )To start, we'll work with the two base classes OBJECT and METACLASS. Try this: metaclass --> methodsThe line above contains three words. The first is the name of a class, so it pushes its signature on the stack. Since all classes are instances of METACLASS, METACLASS behaves as if it is an instance of itself (this is the only class with this property). It pushes the same address twice: once for the class and once for the payload, since they are the same. The next word finds a method in the context of a class and executes it. In this case, the name of the method is methods. Its job is to list all the methods that a class knows. What you get when you execute this line is a list of all the class methods Ficl provides. object --> sub c-fooCauses base-class OBJECT to derive from itself a new class called c-foo. Now we'll add some instance variables and methods to the new class... cell: m_cell1The first two lines add named instance variables to the class, and create a method for each. Untyped instance variable methods (like those created by cell: cells: char: and chars:) just push the address of the corresponding instance variable when invoked on an instance of the class. It's up to you to remember the size of the instance variable and manipulate it with the usual Forth words for fetching and storing (we'll see below how to aggregate objects, which do know their size). We've also defined a method called init that clears the instance variables. Notice that the method expects the addresses of the class and instance when it's called. It stashes those in local variables to avoid stack tricks, and puts them onto the stack whenever it calls a method. In this case, we're storing zero to the two member variables. The init method is special for Ficl objects: whenever you create
an initialized instance using new or new-array,
Ficl calls the class's init method for you on that instance. The
default init method supplied by class object clears the
instance, so we didn't really need to override it in this case (see the
source code in ficl/softwords/oo.fr).
c-foo --> new foo-instanceAnd try a few things... foo-instance --> methodsOr you could type this with the same effect: foo-instance 2dup --> methods --> pedigreeNotice that we've overridden the init method supplied by object, and added two more methods for the member variables. If you type WORDS, you'll see that these methods are not visible outside the context of the class that contains them. The method finder --> uses the class to look up methods. You can use this word in a definition, as we did in init, and it performs late binding, meaning that the mapping from message (method name) to method (the code) is deferred until run-time. To see this, you can decompile the init method like this: c-foo --> see initFicl also provides early binding, but you have to ask for it. Ficl's early binding operator pops a class off the stack and compiles the method you've named, so that that method executes regardless of the class of object it's used on. This can be dangerous, since it defeats the data-to-code matching mechanism object oriented languages were created to provide, but it does increase run-time speed by binding the method at compile time. In many cases, such as the init method, you can be reasonably certain of the class of thing you're working on. This is also true when invoking class methods, since all classes are instances of metaclass. Here's an example from the definition of metaclass in oo.fr (don't paste this into ficlWin - it's already there): : new \ ( class metaclass "name" -- )Try this... metaclass --> see newDecompiling the method with SEE shows the difference between the two strategies. The early bound method is compiled inline, while the late-binding operator compiles the method name and code to find and execute it in the context of whatever class is supplied on the stack at run-time. Notice that the early-binding operator requires a class at compile time. For this reason, classes are IMMEDIATE, meaning that they push their signature at compile time or run time. I'd recommend that you avoid early binding until you're very comfortable with Forth, object-oriented programming, and Ficl's OOP syntax. As advertised earlier, Ficl provides ways to objectify existing data
structures without changing them. Instead, you can create a Ficl class
that models the structure, and instantiate a ref from this class,
supplying the address of the structure. After that, the ref instance
behaves as a Ficl object, but its instance variables take on the values
in the existing structure. Example (from ficlclass.fr):
object subclass c-wordlist \ OO model of FICL_HASH : push drop >search ; : named-wid ( "name" -- )In this case, c-wordlist describes Ficl's wordlist structure; named-wid creates a wordlist and binds it to a ref instance of c-wordlist. The fancy footwork with POSTPONE and early binding is required because classes are immediate. An equivalent way to define named-wid with late binding is: : named-wid ( "name" -- )To do the same thing at run-time (and call it my-wordlist): wordlist c-wordlist --> ref my-wordlistNow you can deal with the wordlist through the ref instance: my-wordlist --> pushFicl can also model linked lists and other structures that contain pointers to structures of the same or different types. The class constructor word ref: makes an aggregate reference to a particular class. See the instance variable glossary for an example. Ficl can make arrays of instances, and aggregate arrays into class descripions. The class methods array and new-array create uninitialized and initialized arrays, respectively, of a class. In order to initialize an array, the class must define (or inherit) a reasonable init method. New-array invokes it on each member of the array in sequence from lowest to highest. Array instances and array members use the object methods index, next, and prev to navigate. Aggregate a member array of objects using array:. The objects are not automatically initialized in this case - your class initializer has to call array-init explicitly if you want this behavior. For further examples of OOP in Ficl, please see the source file ficl/softwords/ficlclass.fr. This file wraps several Ficl internal data structures in objects and gives use examples. |
Ficl String classesc-string (ficl 2.04 and later) is a reasonably useful dynamic string class. Source code for the class is located in ficl/softwords/string.fr. Features: dynamic creation and resizing; deletion, char cout, concatenation, output, comparison; creation from quoted string constant (s").Examples of use: c-string --> new homer s" In this house, " homer --> set s" we obey the laws of thermodynamics!" homer --> cat homer --> type |
object base-class Methods GlossaryThese are methods that are defined for all instances by the base class object. The methods include default initialization, array manipulations, aliases of class methods, upcasting, and programming tools.
Convert an object signature into the signature of the previous object in the array. No check for bounds underflow. |