Mops Roundup | March 13, 2004 | Exit |
| Welcome |
The Mops system has been developed over the past 15 years or more by by Mike Hore, an indefatigable and gifted Australian programmer, as a part-time effort. Given the size of the system currently in distribution, Mops just might be the best Mac software value around. (Let's see, 7.4 MB divided by $0—no, no, that won't work.)
| Mops, Forth, and OOP |
Since Mops is Macintosh-specific it doesn't need a separate API or framework in order to provide necessary Mac Toolbox and system services. In a sense, these are built-in. Thus, you can implement any sort of Mac program and you can build the kind of shared libraries (with callback routines and such) that are rapidly taking over from system extensions. The AltiVec real-number coprocessor in G4 Macintosh models is supported for wizard-level vector mathematical processing. (Ever wonder why Photoshop runs so fast on a G4?)
For those not yet familiar with the pre-OS X Mac's programming services, the Toolbox has been the traditional programming interface for the humongous number of services that the OS provides: windows, menus, and dialogs, to mention the obvious few. The Toolbox is invoked by any one of many defined ‘Syscalls’ that are intercepted by the system when executed. In recent OS versions, the CarbonLib shared library intercepts most of those calls for ‘Carbonized’ programs (those built to co-exist under Mac OS 9 and Mac OS X) and attempts to provide the same or similar services.
| Forth: A History |
Although there is an ANSI Forth standard, the Forth world is actually characterized by a host of variants that differ somewhat from each other and from the standard. So it is reasonable to describe the Mops coding backbone as Forthish, although Mops is certainly a member in good standing. Mops does define many additional Forthish words specifically for Macintosh programming, for convenience, and for object-oriented operations. (Mops can readily be converted to a standards-compliant ANSI Forth compiler, and just as readily back again.)
Mops can be described as a good Forthish language underneath higher-level OOP constructs. One might (but probably shouldn't) say that Mops is Forth wearing an OOP overcoat. Coding, as in all Forth-based languages, is characterized by three major features: explicit stack operations, postfix notation, and language extensibility.
When Forth evolved, the memory available for programs was tiny by present standards. It follows that the curious nature of Forth's notation was not adopted by its inventor arbitrarily or for its novelty value. Now that memory is cheap and plentiful, the compact code produced is equally valued for its speed of execution.
You can download Mops V.4 at The Mops Page. The Web site contains a number of other useful and tutorial items and has links to Forth documentation and resources, including the “Evolution of Forth” paper from which I quote later in this article.
A carbonized version of PowerMops, V. 5.x, is currently available for compatibility with OS X. The ‘old’ standard Mops produces programs for 68K processors, as far back as the Macintosh Plus. (A ‘legacy’ compiler?) The newer PowerMops compiles native for PowerPC machines, and fat applications (for both 68K and PowerPC machines) can be built.
| Naked Stacks and ‘Backward’ Notation |
A second stack, the return stack, contains mostly program return addresses rather than data but is available for use by an experienced Forth programmer. Any unqualified reference to ‘the stack’ always means the data stack, which by the nature of the language must be managed by the programmer. (Assembly language programmers also generally employ one or more stacks in their code, again for efficiency.)
Mops (and every other Forth-based language) uses the common push-down stack, also known as the Last In First Out (LIFO) list. The coding style and ‘grammar’ of Mops, i.e. the rules for forming proper language expressions, are dictated by appropriateness in expressing LIFO list operations.
Forth expert Elizabeth Rather states the case concisely in her paper “The Evolution of Forth” that she presented to the Association for Computing Machinery:
| “ | Forth's explicit use of stacks leads to a ‘postfix’ notation in which operands precede operators. Since results of operations are left on the stack, operations may be strung together effortlessly, and there is little need to define variables to use for temporary storage. | ” |
|
I believe she is saying that stack programming naturally leads to use of a postfix or Reverse Polish Notation (RPN) as being right for the job, and that stack usage radically reduces the need for declared variables. Incidentally, Hewlett Packard handheld calculators also use RPN and—you guessed it—are also stack oriented.
Figure 1 shows three sequential stages or points in the life of one data stack. TOS labels the Top Of the Stack. |
|
| Figure 1: The three stack stages |
|---|
Next is a really simple-minded Mops word definition which we will call Add, for obvious reasons:
: ADD ( n1 n2 -- sum )
+ ;
All that the Add word does is add two values that it expects to find on the stack when it is called. The n1 and n2 symbols in the word's stack comment (within parentheses) indicate that. The ‘sum’ symbol indicates that (by the nature of arithmetic operations) the word will leave the result on top of the stack when it returns to its caller. Note that the only code in the definition is the single add operator, or plus sign.
Now back to the stack diagram; the left-most drawing shows a condition of the stack just prior to a call to Add. The stack content might be values waiting to be used by nested (partly executed) words and information pushed on the stack by Toolbox routines. The middle drawing shows two integers pushed on the stack by a caller of the Add word. These are pushed specifically for the execution of the Add word.
The right hand side drawing shows the result of the operation placed on top of the stack. Note two things: One, that Mops operators uniformly consume or ‘use up’ their operands and, two, the top of stack (TOS) pointer is automatically maintained by Mops. (In general, operands never remain on the stack but intermediate results or more complex procedures do.)
The following is a little more interesting sample of Mops coding.
Value TEMP ( at top level )
.
.
.
: SUM-OF-SQUARES ( n1 n2 -- n3 )
DUP * -> Temp
DUP * Temp +
;
The Value declaration creates a global variable named Temp. The ‘:’ (colon) is the Forth word for defining new words. The colon initiates the definition and is followed by the name of the new word, SUM-OF-SQUARES in this case. (A sum of squares operation produces the squares of two or more values and adds them together.) The comment, in parentheses, is called a stack-effect comment or sometimes stack notation. While stack comments are optional, purely for documentation, they are considered very important in Forth programming. Using a comment makes clear what the defined word, when executed, requires to be on the stack as input and what it will leave on the stack as output. A stack comment may become very complex, and that is when it is needed most.
In this example there must again be two integers on top of the stack when the word is called, symbolized by n1 and n2. Any mismatch between the caller and called word in this respect means big trouble in computer city. The stack will rapidly become incoherent if the called word ‘eats’ some stack data that doesn't belong to it. (The n2 or right-most symbol always represents the top of stack value but that is not significant in this case.) The n3 symbol indicates that the defined word will push one value on the stack as it terminates (the calculated sum-of-squares, naturally).
Note that the stack comment is in no way a map of the data stack. There may be many, many values on the stack below the ones relevant to a given word execution. The lower data items are the business of other words to consume.
The DUP word, a specialized stack operator, duplicates the top of stack value, symbolized by n2, creating a new top of stack cell. The multiply operator—‘*’—multiplies the top-of-stack and next-on-stack values, consuming those values and creating a new top of stack cell containing the product. The Mops-specific ‘->’ operator (called the ‘gazinita’ for serendipitous reasons) is a store operator that moves the top of stack value into certain kinds of variables, such as Temp in this example. So the square of n2 is has been moved off the stack and n1 is now on top. (Of course, we mean the data values signified by n2 and n1.)
The second line of code produces the square of the value symbolized by n1 as before, but then the value of Temp is pushed on the stack, simply by naming it, and the top of stack (n2 squared) and the second on stack (n1 squared) are added together. Both addends are consumed by the add operation and the sum is pushed on the stack as the word's return value. (The SUM-OF-SQUARES word would properly be considered a function in some other languages.) The stack comment's usefulness has been demonstrated I think, even though it is literally just another program comment in the usual sense.
The ‘;’ (semicolon) word ends the word definition. A whole lot was going on in those two little lines of code, no? A somewhat more slick and proper version of the same routine, requiring no variable declaration, is as follows:
: SUM-OF-SQUARES ( n1 n2 -- n3 )
DUP * SWAP DUP * +
;
The very useful SWAP word, another specialized stack operator, swaps the values of the top two stack cells. It consumes nothing. Having explained that much I'll leave the rest as an exercise for you, the student. Just concentrate on each word separately. Dead easy.
There are several ways, using Named Input Parameters and Local Variables, that explicit stack manipulation can be largely hidden or limited, at the discretion of the programmer. At times a required operation can be so complex that use of such magic variables may be required. (The Named Input Parameter and Local Variable are Mops-specific language features.)
Direct benefits of Mops' stack orientation to the programmer include:
| Words, Words, and More Words |
The Mops grammar, consists of a simple notation for defining new words, which you have seen above (: and ; space delimited) and several other equally simple notations. There are many different kinds of Mops words of course, but lexically they are all the same. The OOP constructs require a few additional rules but they are also few and very simple. Indeed, some say that Forth has no syntax because it doesn't need any. The syntactic simplicity leads to the third major feature of the language: user extensibility.
Normal (high-level) Mops words fall into three categories: named data items, named procedures, and defining words. The defining words allow the user to declare named procedures, that is, new Mops words, and also what are called action words. Executable words, or ‘definitions’, are functionally analogous to routines, subroutines, and functions in other languages and sometimes are equivalent to commands in other languages. Much as algebra is the metaphor or model for Fortran, for example, Forth's metaphor is natural-language prose. (Some would say German, with the ‘verbs’ at the end.) Any arbitrary character string can represent a wide variety of functionally different elements. There are virtually no reserved words (: and ; are a few exceptions). The name of almost any word in the dictionary may be redefined.
The Forthish emphasis on the word ‘word’ stems in part from its use of the dictionary construct. All pre-defined Mops words, such as ‘+’ for example, as well as—at some point—all the words that comprise the user's program, are in a dictionary. The dictionary in large part is the user's program. (The interpreter for the dictionary is a matter of a few fast machine instructions.) Conceptually below the dictionary there is a small nucleus that contains the compiler/interpreter's primitives.
All these lexical considerations lead to a fully extensible language in which the user freely makes new words out of combinations of existing ones. This provides the ability to easily factor a program, a very important programming technique.
The veteran Phillip J. Koopman says:
| “ | Writing a Forth program is equivalent to extending the language to include all functions needed to implement an application. Therefore, programming in Forth may be thought of as creating an application-specific language extension. This paradigm, when coupled with a very quick edit/compile/test cycle, seems to significantly increase productivity. As each Forth word is written, it can be tested from the keyboard for immediate programmer feedback. | ” |
Extending the language is reflected in the code compiled for a program. The dictionary, which initially contains only the definitions of the Mops language, is literally extended to include all of the user-program definitions. Execution of the program is tantamount to interpretation of the dictionary, either before or after the program is installed as a standalone, i.e. double-clickable, program.
| Interpreting Mops Programs |
As a postfix, interpretable language, Mops has more in common with PostScript than with the other popular programming languages of today such as C, Java, and Pascal. (It does share Pascal's structured code.) AppleScript on the other hand is an interpretive language but is designed more for ease of use than ‘blazing speed’.
One may test the definition of, for example, the SUM-OF-SQUARES word defined above, prior to incorporating it into any program source, by entering it into Mops' data entry window and making calls to it, in interactive interpretive mode. Very confidence making. As shown in Figure 2 below we have entered the sum-of-squares definition into Mops' data-entry (and editing) window, which is the portion below the horizontal line.
| Even though we have selected the definition and pressed the «enter» (not «return») key, which enters what has been typed, the stack remains empty, as shown in the upper window. |
| That is because the SUM-OF-SQUARES definition has been compiled into the dictionary as a Mops word. (The colon word puts Mops into compile mode.) |
| Figure 2: Entry of Word Definition | ||
|---|---|---|
In Figure 3 we enter two values, 12 and 24, and call SUM-OF-SQUARES simply by naming it. (Followed by the «enter» key). Mops, in interpretation mode, places the numeric values on the stack (standard behavior) and searches for a match for the name in the dictionary—not far to look in this case—and executes the matching word when found. Finally we see the sum-of-squares result on the stack.
| Maybe if we had some sort of alien high-speed vision we could have seen the two numbers on the stack before they were eaten by the SUM-OF-SQUARES word. We could have entered the stack values by themselves of course and seen them there before invoking SUM-OF-SQUARES |
| As can be seen, when a more complex word definition has been entered in the dictionary, you can sit at the keyboard and test it all day long with various values and combinations of inputs. |
| Figure 3: Entering Values for Testing | ||
|---|---|---|
The advantage of being able to test small pieces of a program independently of most other parts is obvious.
| Mops' Object Orientation |
The following three subsections present a somewhat abstract description of the ideas underlying OOP. If you are already familiar with OOP concepts you can skip down to the section entitled “Example of a Class Declaration”. The concept description follows the view of Adele Goldberg, sometime member of Xerox PARC and developer of Smalltalk, who, if anyone, can be called OOP's inventor. In any case she was certainly one of the PARC staff members who “dealt lightning with both hands”, in the memorable words of Alan Kay.
| Objective |
| Model |
The above quotation implies a programming paradigm of a higher level than those provided by Forth, Modula, Algol, and Pascal—one that is able to accommodate the lesser paradigms without distortion, as witnessed by the seamless combination of Forth and OOP in Mops.
| Basic Concepts |
Let's start out with a practical distinction: A class can be thought of as a source-program entity by which an object, a runtime entity, is created. A class definition describes a class of objects. One or many identical objects can be created from one class definition, although their data values will normally differ during execution. Static objects, compiled into the dictionary, must be uniquely named. Individual objects are said to be instances of their class.
An object consists of some amount of reserved memory used for data and a set of operations, or methods. The nature of the data and operations depends on what the object represents, which may range from an elementary data object to an on-screen window, up to a complex model of some entity in the problem space. (The Mops demo program, for example, produces fascinating patterns that model a set of mathematical expressions.) The object's data is logically packaged together with its methods, i.e. with the code that implements the necessary operations on the data. This is known as logical data-code encapsulation.
Another object can request that a particular datum be stored, retrieved, modified, etc, by sending a message to the object containing the relevant data structure, which in this context is called the receiver. The receiver recognizes only those messages that correspond to its set of methods. The crucial property of an object is that its memory can only be accessed by its own methods. The crucial property of messages is that they are the only way to invoke an object's methods.
The combination of these two properties ensures that the implementation of one object cannot depend on any feature of the implementation of any other object. This is not only a fundamental gain but it is also of practical benefit during program development and especially in program maintenance. While developing even a small program, the freedom to try different implementations of one class of object without affecting the others is a joy.
Note that a Mops object is in general a composite structure, in that it contains one or many lower-level objects that make up its data structure and, implicitly, add to its methods. Thus according to OOP rules, a composite object can and always does send ‘internal’ messages to its contained objects. This internal messaging may occur to a number of levels. Thus in respect to methods, a high-level object may be thought of as a cascade of operations flowing from many objects in addition to the object itself. This is a quite significant point, however informal its description.
The sending of internal messages can be thought of as a chain that ultimately reaches back to methods formulated in primitive operations that are close to machine code. Internal messaging combined with inheritance provides a very high degree of information hiding. The programmer never sees the definitions for most of the methods invoked by his program; he need only understand and know the names of the methods he calls upon.
Finally, in Mops a message may be sent to an object from code that itself is not part of an object, that is, from an ordinary Mops word.
| Example of a Class Declaration |
The following is an abbreviated version of a class definition used in an actual game implementation. (The symbol ‘--’ in the stack notations separates stack input and output. Thus the stack notation ‘( -- )’ says that no stack value is expected and no value is pushed, or produced, by the word.)
:class SCOREKEEPER super{ Object } \ Defines the class name
\ and superclass
9 wArray BOARD_ARR \ Declares a 9-cell word-array Ivar
:m CLEAR_BOARD: ( -- )
clearX: board_Arr \ For use by the post-game cleanup word
;m
(* ------------- Elementary methods of the class -------------- *)
:m FINDZCELL: ( -- idx | t ) \ Locate first empty (zero) cell.
9 0 DO i at: board_Arr \ Loop thru the array and, if a
0= IF i UNLOOP EXIT \ zero cell is found, push index or
THEN LOOP true \ return a true flag for none found.
;m
:m CHKZCELL: ( -- b ) \ Check cell location (celLoc) for
celLoc at: board_Arr \ zero content. Return true if zero;
0= IF true ELSE false THEN \ false otherwise.
;m
:m PUTOMK: ( -- ) \ Update state value of board array.
Omark celLoc to: board_Arr \ Store O-mark value in cell celLoc.
;m
:m PUTXMK: ( -- ) \ Update state value of board array.
Xmark celLoc to: board_Arr \ Store X-mark value in cell celLoc.
;m
:m @CELLVAL: ( idx -- n ) \ Fetch value at passed-in cell index
at: board_Arr \ (idx) and return value on the stack.
;m
(* A number of lengthy methods and words have been omitted. *)
;class ( scoreKeeper )
scoreKeeper BOARDRECORD \ Instantiate scoreKeeper-class object
The second line is very significant. It declares a data object, a data structure of class wArray, of size 9. We need not define that class since, like so many others, the definition already exists and only the object declaration is required. (As you will see, methods specific to that object come along ‘for free’ also.) So our instance variable, or ivar, is a 9-cell indexible word array.
The first method definition defines itself in terms of ‘clearX: board_Arr’ which is actually a message to the internal data object board_Arr for which the method clearX: is already defined. (The ‘X’ in ‘clearX:’ has nothing to do with the X player in Tic Tac Toe; it's just part of the built-in Mops method name.)
Note that method names must end in a colon (:), the only, if not one of the very few arbitrary lexical rules in Mops.
The next method definition, FINDZCELL:, shows a combination of ‘code’ and an internal message to board_Arr. Objects of its class know how to execute an ‘at:’ method, which takes an index value, supplied by the DO loop as input.
The PUTOMK: method, used when the computer makes a move, is a bit interesting in that it takes nothing off the stack but pushes the O-mark code value and the (global) cell location, celLoc, on the stack to accompany the to: message. The to: method in every wArray-class object's repertoire requires these two values; what to store and where to store it. PUTXMK: is called when the player clicks on a square. (A different object, which owns the playing field, writes the corresponding X and O marks on the screen.)
The ‘;class’ word terminates the definition (just as ‘;’ terminates a word definition, and ‘;m’ terminates a method definition). Thus the final line in the example is not part of the definition at all but is a sample declaration of an object instance of the scorekeeper class with the name BoardRecord. An object so created, i.e. by declaration, is static and is compiled into the Mops dictionary of the program it is part of. (Most, and often all, of the objects in a Mops program are static. Dynamic objects require a little extra work.)
| Mops Pre-Defined Classes |
Mops' pre-defined classes for graphical user interface (GUI) objects hide the grimy, low-level coding required, largely by virtue of the OOP inheritance mechanism. The commonly used WINDOW+ class, for example, could be said to reach back to many other, simpler class definitions for parts of the complex data structures and the methods it needs. That is to say, an instance of the WINDOW+ class is a highly composite object. The programmer can however write a fairly simple WINDOW+ subclass definition (which inherits all of the WINDOW+ data and methods) in order to override, or modify, one or more features of its superclass.
It is often possible, however, to simply declare an object to be of the WINDOW+ class, for instance, and then send a few messages to it, for example one that causes it to become ‘alive’ in the Toolbox. The class definitions for pre-defined objects need not appear in the source program since the definitions in question are normally pre-loaded transparently before the source program is read in. (Some are part of the core group in the dictionary and are always there.) The programmer must, however, be very familiar with the pre-defined class definitions for the objects he uses in order to understand the functionality provided and to know how to send proper messages.
Standard Macintosh window behavior, such as dragging, growing, and updating, is handled automatically by Mops' window classes, freeing the programmer to focus on application-level problems rather than constantly rewriting system-level code. To an extent the same can be said of all the Mops Toolbox classes.
Mops also supports a View object that is widely used in conjunction with WINDOW+ class objects. A view basically defines a rectangular area within a window in which drawing can take place. A view can have child views and has other interesting properties. The View construct is supported also by most Macintosh APIs and frameworks, such as MacApp, TCL, PowerPlant, and Cocoa.
So, how are objects created in general? As we've seen, every object is an instance of a class of objects defined in the source program by a class definition. When it is appropriate that an object be statically allocated it can be created by a simple object declaration that we've seen above (BOARDRECORD) the declaration is said to instantiate an object of its class. The benefit of a static object is that everything about it is simple and trouble-free (relative to dynamic objects). Also, message-to-object binding is always early (fast). The potential downside is that it is compiled into the dictionary (‘locked in’) and continues to occupy program space even though it may have outstayed its welcome, a possible concern for big programs.
The alternative is a dynamic object, which is created at runtime on the program's heap. The picture here is a little mixed. The recent versions of PowerMops (V. 4.x and above) provide very simple mechanisms called References to create and manage dynamic objects, with most interactions handled transparently. In Mops (68K) however the program must obtain a object handle from the Toolbox for the object-to-be (an objHandle declaration) and then issue an object creation statement (a newObj: message to the handle object). Objects so created are referenced by their handle name or index. In the latter, ‘traditional’ case a good bit of care is needed in managing the relocatable blocks of heap storage normally allocated for dynamic objects.
| Examples of Mops Pre-Defined Classes |
:class VAR super{ longword }
:m +: inline{ ^base +!} ;m
:m -: inline{ ^base -!} ;m
;class
Note that there is no data-object declaration for the object's ivar. It is not needed because the superclass Longword supplies it. The +: and -: methods, for incrementing and decrementing respectively, seem an insufficient repertoire of operations on a variable. Again, like the Lone Ranger, the superclass Longword comes to the rescue. Longword has already defined Get:, Put:, and Clear: among a few others. So VAR implicitly has those methods (inheritance). Longword is the generic superclass for many other elementary data classes and thus defines all of the methods common to those subclasses. The VAR class definition is characteristic of many of Mops' basic classes in its reliance on inheritance from a ‘generic’ superclass.
Near the other end of the spectrum is the Menu class definition. For practicality only a few of the 25 or so methods are shown below, including a few universally used ones. (The example is drawn from PowerMops V. 4.x. The code might change somewhat in later version of PowerMops, but the example is adequate for illustrative purposes.)
:class MENU super{ x-array }
68k_record
{ int RESID \ Resource ID of this menu
var MHNDL \ Handle to menu-heap storage
}
:m ID: inline{ get: resID} ;m
:m PUTRESID: inline{ put: resID} ;m
:m INIT: ( xt1 ... xtN N resID -- )
put: resID put: super ;m
:m NEW: ( addr len -- ) \ Create menu with title. Nonresource based.
str255 get: resID swap NewMenu
put: Mhndl ;m
:m GETNEW: \ Resource-based menu
get: resid GetMenu dup 0= ?error 127
put: mHndl ;m
:m INSERT: \ Inserts the menu in the menu bar.
get: Mhndl 0 InsertMenu ;m
:m GETITEM: ( item# -- addr len ) \ Gets string for item n
get: mhndl swap 1+
buf255 GetMenuItemText buf255 count ;m
:m PUTITEM: { item# addr len -- } \ Replaces menu item string
get: mhndl item# 1+ addr len str255
SetMenuItemText ;m
:m CHECK: ( item# -- ) \ Checkmark an item.
get: Mhndl swap 1+ -1 CheckItem ;m
:m UNCHECK: ( item# -- ) \ Remove checkmark.
get: Mhndl swap 1+ 0 CheckItem ;m
;class
The superclass x-Array (array of execution tokens) implies that the class definition inherits an x-Array in its data structure, but the class definition need not fix the size of that array. The size specification can be, and usually is, deferred to the object declaration that instantiates an object of the menu class. (This is not an intuitive feature of the array-type classes.) The size specification will determine the number of menu items for a given menu object, so the program obviously needs to set that value itself.
In normal practice, all programs will send a message to the Init: method. It associates actions with all items in a menu object—for example the program's response to the user's selection of the New item in the File menu. Also, the program will always call either New: for a non-resource-based menu or getNew: for a resource-based menu. Both methods pass a menu record to the Toolbox. Both methods are fully supported.
For a resource-based menu, the programmer literally draws pictures of each menu, as a ‘menu’ resource, using a resource editor such as Apple's freeware ResEdit. It's dead easy, and I can't draw my way out of a paper bag. The resource-based approach is much simpler to program and is pretty generally used. The average Mops program will need a couple of other resources anyway, so adding the menu resources is no pain.
The getItem: and putItem: methods lets you play with menu item names on the fly, so to speak (and may completely disconcert your user).
Many programs will call the Check: and unCheck: methods; the former puts that neat little checkmark against the name of a clicked item and the latter undoes it. The ‘1+’ word is a shorthand for adding 1 to the number at the top of the stack.
Note that the intelligible names in upper and lower case, such as GetMenu, InsertMenu, SetMenuItemText, and CheckItem are all system calls, or Syscalls. They invoke a Mac Toolbox service. It is important to know that, unlike names in the Mops domain, Syscall names are case sensitive. (Very much so. The Toolbox is quite particular.)
| Subclasses and Superclasses |
Thus, every user-defined class inherits both ivars and methods from its chain of superclasses. (See Inheritance and Overriding below.) This is particularly true of a user-defined class that interacts with the operating system (through the toolbox or otherwise) and typically has several Mops pre-defined Toolbox classes in its lineage. Such a class would normally be referred to as a subclass, but that is strictly a matter of viewpoint.
| Inheritance and Overriding |
On a practical note, the greater the number of methods that are inherited rather than defined at the subclass level the smaller the resulting program (all else being equal).
| Multiple Inheritance |
| Message Binding |
| Conclusions |
When I first encountered Mops, the ‘backward’ notation of its Forth heritage was an annoyance to say the least, I got over that fairly soon. The stack programming aspect, once I was pretty well acquainted with it, struck me as very economical, efficient, and so on. I remain however a fan of object-oriented programming, and that is where Mops appealed to me. I liked the clean way that OOP is implemented in Mops. Much of it is intuitive.
The Mops system includes a manual that runs to over 370 pages, in four parts. Except for the tutorial Part 1, it is not intended to cover the Forth language aspect of Mops in any comprehensive way. I found that I needed a good Forth reference manual to fill in the gaps, and I can recommend the Forth Programmer's Handbook offered by FORTH Inc. as an excellent value. All in all, for someone who likes or would like to try OOP (or Forth for that matter), I think that Mops is a good ‘buy’.
Copyright © 2002, Ed Williams is emailable
| ⇧ |
|---|
| http://PowerMops.com/MopsRoundup |