Mops 2.0
Volume Number: 7
Issue Number: 9
Column Tag: Jörg's Folder
MOPS 2.0 is "Savy" 
By Jörg Langowski, MacTutor Editorial Board
“Object Forth news - MOPS 2.0”
We’ll interrupt our C++ course for one or two columns; I’d like to show you
some other interesting things that I recently received in my mail. This month we’ll
look again at one of the object-oriented Forth systems that I’ve been covering off and
on. Michael Hore, from the Northern Territories of .oz land, far away from electronic
mail and other goodies of modern civilization, has done a major upgrade of his MOPS
system, version 2.0. I pre-announced it a couple of columns ago; now I received the
disk in the mail, was impressed, and would like to share my impressions with you.
First of all: I can put the example program and the source text on the source code
disk; but the complete Mops system fits just barely on an 800K floppy (in compacted
form), and takes 1.6 MBytes on my hard disk when de-compacted. Therefore, I’m
sorry, but you’ll have to get Mops through the network if you are interested to develop
programs with it. Which I hope you are after reading about it. For the moment, I don’t
know whether Mops is available through one of the Macintosh software depositories. I’ll
try to put it on sumex-aim.stanford.edu; you may also look for it on
oddjob.uchicago.edu, where Bob Loewenstein has put his Yerk system, which is very
similar. People who have problems getting Mops can always contact me,
langowski@frembl51.bitnet, and I’ll send you a copy.
If you want to write Michael Hore directly, this is his address:
Michael Hore, c/o MAF, P.O. Box 821
Nhulunbuy, NT 0881, Australia
Mops basics
Enough of a preface - we’ll quickly look at Mops, see what it does and how one can
develop Macintosh applications with it. Mops is an object-oriented programming
system based on Forth. A great part is a re-implementation of the NEON development
system, which long-time subscribers of this journal might still remember. NEON,
introduced in 1986 by Kriya Systems, was in my opinion one of the most successful
attempts to create a simple object-oriented development system for the Macintosh,
which allowed to create applications quickly and without the overhead that a
powerful-but-huge system like MacApp required. Unfortunately, due to buggy first
implementations it didn’t succeed in getting too large a customer base, and shortly after
releasing the first stable version 2.0, the company more or less dropped the product.
Now, some people had gone quite far in developing applications in NEON, and
when they slowly realized that they couldn’t expect customer support or upgrades from
the original publishers anymore, rather than totally changing the implementation of
their programs, they tried to make NEON work on their own. Bob Loewenstein, who
you’ve read about in this column, modified the NEON kernel so that it would work on the
new Macintoshes with 68020 and 68030 processors, and worked very hard on Kriya
Systems in order to get NEON’s source code released in the public domain. This took
about two years, but at the beginning of this year, he finally succeeded (you read about
it). He called his upgrade of NEON Yerk, from the Yerkes observatory where he worked,
and Kriya systems allowed him to release the NEON source code ‘for non-commercial
use’, as long as it is not called NEON, but Yerk. So for all there is, Yerk is the
semi-official successor of NEON. It is available from oddjob.uchicago.edu through
anonymous FTP, directory ~pub/Yerk.
Michael Hore took another, more radical route, to get around the problems with
NEON on the 68020 and with its customer support. He ‘simply’ re-wrote it. He
implemented a new subroutine-threaded Forth kernel (for non-Forth readers, this is a
Forth that creates directly-executable 680x0 machine code and not interpreted code),
which works 4-5 times faster than the original Forth that NEON was based on. Then he
made quite a few modifications to the implementation of the object support, and to the
language syntax. The result, which is almost NEON- (excuse me, Yerk-) compatible,
he called Mops, for Michael’s object programming system.
Some excerpts from the Mops documentation illustrate best what he did:
Mops philosophy (Michael Hore)
“The basic idea behind Mops, originally, was of a reimplementation of Neon to
compile subroutine-threaded code, which would run faster than the Neon indirect
threaded code. The goals have expanded somewhat in the direction of some modest
evolution of the language itself, and full System 7 compatibility.
The speed goals have certainly been achieved. Mops not only compiles
subroutine-threaded code, it also compiles straight native code for common sequences.
A certain amount of local optimization is also done. The result is that Mops code should
run 4-5 times faster than the equivalent Neon code. Some benchmarks may run
outrageously fast under Mops, due to the optimization. The Sieve benchmark, for
example, runs in about 3 seconds on a Mac Plus (Neon took 21). Floating point, if a
68881/2 FPU is installed, is very swift indeed. Compilation speed is also much
improved, not only by the faster underlying execution speed but also by the 8-thread
dictionary structure.
Seeing I was originally intending Mops just for my own use, not wanting to tread
on Kriya’s toes, I also took the opportunity to incorporate a number of other
improvements to Neon. At least to me, they were improvements. Others may disagree,
e specially if they hit trouble converting Neon code. However I will attempt here to
document the differences between Mops and Neon, to try to make life easier for anyone
changing over.” (end of excerpt)
Mops syntax
An object-oriented programming language must allow to define a class
hierarchy, with methods and variables local to each class. Also, there must be a
mechanism to send messages to an object, and implement early (compile-time) and late
(run-time) binding of the message to the class of the object. Neon implemented this
quite elegantly, and Mops does it even better. I have prepared a very simple example -
a resizeable window with zoom box and scroll bars, but nothing in it - to illustrate
Mops programming.
The window is an object of the class CtlWind, which is defined in the Mops source
code. The definitions are included in the listing; however, lower level classes are not
(for space reasons), so you must imagine by yourself that there are such things as
scrollbar objects.
A class definition looks like the following:
\ 1
:class myClass super{ mysuperclass }
\ instance variables (always private)
ptr a
int b
private \ the following methods are private
:m methodA: doThisinSecret ;m
:m methodB: doThatinSecret ;m
public \ publicly accessible methods
:m methodC: doThis ;m
:m methodD: doThat ;m
;class
and for creating an object myObject of myClass, one simply writes myClass myObject.
The superclass is defined by super{ superclass1 superclass2 }; Mops supports
multiple inheritance from a list of superclasses. You also see that Mops allows private
and public methods; private methods can only be used on self and super, thus within the
class hierarchy. Instance variables are always private, as they have been in Neon. You
must write access methods to manipulate them from the outside. I like that concept, and
if one wrote C++ that way, it would certainly be more readable. (Yes, I know: I haven’t
done that in my examples, either. But it would be a nice idea.)
Method names must finish with a colon (error otherwise), to make them look
like nice messages. A method call is made by e.g. methodC: myObject, and when
parameters are passed to the method, like in any good Forth system they are put on the
stack by preceding the method call with them: par1 par2 methodD: myObject.
Let’s look at the example in detail. The first thing one notices is the striking
simplicity of Forth code lines over those from other languages. For conditionally
including a file, if some definitions are not present, we need to write at least three
lines in C++, e.g.:
/* 2 */
#ifndef __TYPES__
#include
#endif
In Mops, you simply write need Ctl if you want to include the Controls definitions
in case they aren’t there yet.
The ‘object pointer’ objPtr is another interesting concept in an object Forth
system: this is a pointer to an object whose class is determined at compile time. When
you store a pointer in this variable, it is checked whether it really points to an object
of the given class, and an error message is issued if not. That way, we can use early
binding for sending messages to the object that the pointer points to, which is more
efficient.
In the following lines you see that Mops can be used for defining :proc routines,
which are Forth routines that do all the necessary register housekeeping to be called
from toolbox routines (e.g. they can be used as filter or control action procedures).
For the remaining code, if you don’t know Forth or Neon, it might not be evident
to read it. Try anyway, and keep in mind that arguments always precede a function call.
In the method definitions, you see lots of examples of message passing, for instance the
put: and get: messages which put a value into a variable or get it back, or enable: and
disable: which can be sent to a scroll bar or a window. Remember that self and super
are used to designate the class that the method is defined in, or its superclass (this or
inherited in C++). The word CFAS{ is used to put a variable number of routine
pointers on a stack. For instance, the line
\ 3
CFAS{ lnup lndn 10up 10dn null } actions: vv1
will put five addresses on the stack, the first four being pointers to the handlers for
the up and down arrows and the page regions of a scroll bar, null being a do-nothing
routine (we don’t want any special handler for the thumb region). actions: vv1 sets the
action vectors of the scroll bar vv1 to point to these routines.
Other Mops features
The example only gives a little glimpse of what you can do with Mops. There are
many more new features added, I’ll list a few from the documentation that comes with
Mops:
Object handles - “ to access the object, the method obj: anObjHdl returns a
pointer to the object, and also locks the handle so that the object won’t be
unceremoniously moved while we are doing things with it. Remember to unlock:
anObjHdl when finished. When you are finished with the object, send Release: anObjHdl.
This will automatically cause a late-bound Release: to be sent to the object itself, before
its storage is released, in case it has some heap storage of its own.”
Late binding - “Late binding works exactly as in Neon. I had one problem in
that I wanted to use [ and ] to replace <[ and ]> to turn compilation off and on, since this
is what just about every other Forth-based system uses. So now [ and ] do double duty.
If they follow a method selector, they cause a late bind as in Neon. In any other context
they turn compilation on and off. To help avoid confusion, we have added another syntax
for a late bind: method: ** to bind to whatever is on the top of the stack at run time.
Thus instead of method: [ (some code) ] you can now put (some code) method: ** and
maybe not get so confused with [ ... ] meaning two different things. Still, [ ... ]
sometimes looks neater, so by all means feel free to keep using it.”
Optimization - “(the) optimization technique has worked well in practice,
and generally gives around a 15% improvement in execution speed and a 10% reduction
in code size. Of course, some common code sequences are improved much more than
this.”
Large arrays - “Objects in indexed classes may now have more than 32K
elements. You have to declare the class as “LARGE”, thus:
\4
:class SomeClass super{ someSuper } n indexed large
Indexing operations on LARGE classes will use 32-bit arithmetic, which will
slow accesses very slightly (on 68000-based machines only).”
Profiler - “Another new whiz-bang feature in C and Pascal compilers is a
profiler, to give statistics on time spent in various lines of code, and also the number of
times they have been executed. Well, anything that can be done in C or Pascal can be
done more easily in Mops [my underlining - JL], so we had to have a profiler too.
Because of the hierarchical nature of the language, it seemed to make the most sense to
base profiling on a given word, whose definition is profiled. This way bottlenecks can be
tracked down interactively, and you can zero in on the places of interest, rather than
have to wade through a mountain of useless information. Anyway, it was easier to
implement this way.”
System 7 support and AppleEvents - “Mops is now “System 7 friendly”.
Among other things, this means that it recognizes AppleEvents. Mops handles the “core”
AppleEvents: OpenApplication, OpenDocuments, PrintDocuments and QuitApplication.
These have to be available in the nucleus, so that the nucleus can be properly System 7
friendly. We have provided handlers for these AppleEvents, as required by Apple, and
also four corresponding vectors so that your application can customize things.”