Andrew McKewan uses the Forth scientific library (FSL) as example. He argues that it was necessary to standardize floating-point in ANS Forth before the FSL could be written. Even if that were true (of which I am not convinced), the cases differ significantly: A floating-point implementation written in Forth-83 would have been unacceptably slow for many purposes; OTOH, many object-oriented models can be implemented in standard Forth efficiently enough to be useful. (BTW, this is a major strength of Forth over many other languages: C, Pascal, Ada etc. require language changes and new compilers to accomodate object-oriented programming; in Forth every programmer can do it.)
As a counter-example, consider the case of locals: Even though the
commitee standardized the syntax LOCALS| this read can you
|
, many people use the syntax { you can read this
}
. This syntax can be implemented without performance penalty
in standard Forth ([hayes92], http://www.complang.tuwien.ac.at/forth/anslocal.fs).
What would such an object-oriented library look like? It should have as few system dependences as possible. This excludes libraries for dealing with windowing systems, which appear to be the most popular application of object-oriented technology in Forth. The library should deal with problems that are hard enough to make reinventing the wheel unattractive. A look at the standard libraries of other object-oriented languages should provide some inspiration.
OTOH, what's so bad about having no standard object model? We don't have a standard array or structure model, either, because we can build what we need when we need it, at little cost.
selector object
syntax, which makes it unnatural to pass objects on the stack. This
syntax could make it easy to pass the selector on the stack, but that
is rarely needed (and the Neon model makes it hard to pass the
selector anyway, see below).
The Neon model uses the following syntax for dealing with objects passed on the stack:
selector [ code ]
code
must produce an object reference, which is
then consumed by the whole construction. This syntax reduces the
extensibility (see below), and offers no advantage over the more
conventional (and therefore easier to learn) syntax
code selectorYou can also use
code selector [ ]in the Neon model. But that's just more complex; and mixing this syntax with the other variants makes the usage less consistent and thus harder to read.
E.g., suppose for some reason that you want to tick a selector to
get an execution token that you can EXECUTE
or
COMPILE,
later. How do you pass the object to that
selector? You cannot use the natural way: to pass it on the stack;
instead, you have to manipulate the input stream. (Charles Melice
pointed out that you can get an xt for a word that takes an object on
the stack and passes it to a selector in the following way:
:noname selector: [ ] ;
).
Once you have managed to deal with the input stream, the real
trouble starts: All selectors defined with McKewan's implementation of
the Neon model are state-smart. I.e., what they do, depends on the
contents of STATE
when the selector is invoked. So you
have to make sure to set STATE
right for every place
where such a selector might be invoked. If you fail in that, the
resulting bug usually is hard to find.
This should demonstrate the trouble with parsing words in general and with the Neon model in particular.
We could choose to forbid ticking (and POSTPONE
ing)
selectors, or we could choose a model that does not have this
problem.
The Neon model allows sending (a message with) any selector to any object (let's call such models Smalltalk-like). This is considered essential by some. However, it also makes it harder to create efficient implementations.
In contrast, some models (let's call them Java-like) allow sending only those selectors to an object that were explicitly defined for the class of the object or its ancestor classes. Some also have multiple inheritance or Java-like interfaces.
In practice, you can program the same things with the Smalltalk-like and Java-like models. In the Java-like models, you have to define the selector in a common ancestor class (or common interface) of all objects that use the selector. If you fail to do this, and send a message to an object, for which the selector was not defined, the result in a straightforward implementation of a Java-like model is a crash or the invocation of an unrelated method (with a little additional code and run-time overhead, a Java-like model can produce a decent error message in this case, though); in contrast, with a Smalltalk-like model you get a run-time error (message not understood).
Concerning implementation, a Java-like model can be implemented easily and efficiently using a technique that C++ implementors call virtual function tables. For a Smalltalk-like model on an interactive system like Forth, using virtual function tables is much harder [vitek&horspool96]. Indeed, AFAIK, no Smalltalk-like Forth extension uses virtual function tables; they all use searching methods that are significantly slower.
Proponents of Smalltalk-like models argue that most selector lookups can be resolved at compile-time, eliminating the searching overhead. However, studies of programs written in full object-oriented style (in other languages) show that message sends occur every 60 instructions (median), and even complex analysis algorithms leave a significant number of them unresolved (usually, because the message send actually does invoke different methods at run-time) [diwan+96].