While there is general agreement that Object-Oriented Programming (OOP)
can be a valuable technique for solving certain types of problems,
Forth currently has no standard extension for this. A standard
extension would provide the following benefits:
1) Code written by anyone using a Forth ANS OOP extension could be
easily read, understood, and possibly used in whole or in part, by
others.
2) A standard set of classes could be created and grown over time.
This would provide a rich set of useful classes for constructs such as
object lists, dynamically sizable strings with many string methods, and
much more.
3) More programmers would be inclined to try Forth since many of
today's programmers insist on object programming support from any
language they use.
PROPOSAL
Adopt a long-existing "unofficial standard" OOP extension, based on the
once commercial product Neon. This basic syntax has been in use for 22
years and has since spread to four other Forths on two different
platforms (Yerk, Win32Forth, Mops/PowerMops, and Andrew McKewan's
ANS-compatible extensions. Platforms are Windows and Macintosh.).
TYPICAL USE
The basic Neon Model (hence referred to as NM) is inherently simple to
learn and use, yet offers a rich set of of OOP features allowing for
full expression of object-based solutions to problems using Forth. As
an example consider the following definition and use of a point class:
:CLASS Point \ :class begins the definition of a class, here the class
name is Point
VAR x \ declare two instance variables, embedded objects named x and
y of class VAR
VAR y
:M PUT: ( x y -- ) put: y put: x ;M \ :m begins the definition of a
method, ;m ends it
:M GET: ( -- x y ) get: x get: y ;M
:M CLASSINIT: 10 20 put: SELF ;M \ a method named classinit: will
automatically be invoked
\ upon object (or ivar) instantiation
;CLASS \ ;class ends the class definition
Point p \ instantiate a named object in the dictionary, named p
get: p \ will return 2 items to the stack, the numbers 10 and 20
REMARKS
A long-standing debate over the form of message sending has existed.
The two popular forms are as follows:
1. parameters-message-object
2. parameters-object-message
1. and 2. are similar but slightly different. The placement of the
parameter(s), if any, is generally agreed upon.
I would comment that there has been an over-emphasis on the importance
of the above. In this submitter's opinion there are many, many more
issues to carefully consider when selecting an OOP standard. However,
because of the history of the debate, I believe that I must address
this at the outset of this proposal.
Some have maintained that 2. is more "Forth-like" because it places the
noun (object) before the verb (message). However there appears to be
no problem with other ANS Forth constructs such as "100 TO MYVALUE"
which has the same ordering as 1. (verb preceding noun).
Further, this proposal (NM) is very flexible as-is and can easily
handle the situation where an object is passed via the stack to another
word which needs to send a message to that object. Consider the
following four different ways of handling this situation:
a) : test ( obj -- ) message: ** ;
b) : test ( obj -- ) message: [ ] ;
c) : test { obj -- } message: obj ;
Comment about c). A popular form of locals is used here. It has been
repeatedly demonstrated that, especially with the many optimizing
compilers in use today, locals can be as fast or faster than stack
manipulation.
d) : test ( obj -- ) message: <classname> ;
Comment about d). This uses a feature of the NM to easily and simply
invoke early binding, providing a significant speed increase, by
sending a message to the class with an object of class <classname> on
the stack. Of course this can only be used when the class of the
object is known.
Hopefully the above examples will put to rest any still-existing
concerns over the ordering of message and object. The NM can do both.
The NM presented, although simple and powerful as is, can be extended
even further in the future should the need/desire arise. These
extensions could include multiple inheritance, temporary objects
(existing only within one colon definition), class variables and class
methods, private methods and ivars, and much more. A precise example
of how this may be done exists in the Mops and PowerMops Forth
implementations, the full source code for which is freely available.
Other significant design features of the NM are listed as follows:
- Default early binding for speed. When defining methods of a class
that call other methods of that same class or superclass (which is
simply good factoring practice), it makes a lot of sense to use early
binding. This is the default, which the compiler will automatically
invoke, so there is no extra work for the programmer.
- Proper support of embedded objects as class instance variables
(ivars). One need only declare <classname> <ivarname> within a class
definition. Subsequent objects or ivars will then have these embedded
objects and the objects will be properly initialized and respond to
messages valid for its class.
- Objects can be created as named dictionary objects or unnamed objects
in the heap.
- As is done in SWOOP (SwiftForth's object programming extension), when
defining a class the message name, method definition, and association
of that method with that message, is all done in one step.
Additionally, also as in SWOOP, there is no need to declare that a
superclass method is being over-ridden. This is implied by the simple
existence of a new method definition of the same name.
- Also like SWOOP, the so called "Smalltalk-like" class/message-name
ordering scheme is used. While slightly slower than a virtual table
(VT) scheme, so called "Java-like", this submitter believes that the
flexibility of the Smalltalk-like scheme is the best design choice for
a Forth standard. The VT is very restrictive and, while it may work
for a single programmer, getting the entire Forth community to agree on
a common class/message ordering scheme would be difficult or
impossible.
REFERENCE IMPLEMENTATION
An ANS-compatible NM implementation, complete with full documentation
and an example class library, is available at the following URL:
http://members.talkamerica.net/douglas...@talkamerica.net/
Use file CLASS12F.zip for Windows, and CLASS12MF.zip for Macintosh.
The content is identical, but the formatting is different.
Note that this implementation is based directly on the work of Andew
McKewan circa 1996-1997.
TEST CASES
A complete set of test cases are provided in the Reference
Implementation. See file TEST12.F.
EXPERIENCE
As mentioned above, the NM has existed for 22 years. Current
implementations, with nearly identical syntax to the provided Reference
are Win32Forth and PowerMops.
Respectfully submitted,
Douglas B. Hoffman
16 Jun 2006
> PROBLEM
> While there is general agreement that Object-Oriented Programming (OOP)
> can be a valuable technique for solving certain types of problems,
> Forth currently has no standard extension for this. A standard
> extension would provide the following benefits:
What is it exactly that you propose?
I have no problem loading either class.fth or objects.fs in iForth.
Both seem to be ANS Forth (or use only 'sane' extensions). I seem to
recall that many or most Forths can load these two files correctly.
So in fact we already have two standard ways of using objects in Forth.
No special kernel extensions (that would need standardizing) are necessary.
The performance of both packages is also decent:
-- ----------------------------------------------
FORTH> in performance
Performance test of CLASS.FTH
var x1
: t1 ( u ) TIMER-RESET 0 ?DO get: x1 drop LOOP .ELAPSED ;
: t1b ( u ) TIMER-RESET 0 ?DO get: [ x1 ] drop LOOP .ELAPSED ;
#8000000 t1 0.064 seconds elapsed.
#8000000 t1b 0.245 seconds elapsed.
xcounter heap-new constant x1
: t2 ( u ) TIMER-RESET
0 ?DO x1 val drop LOOP \ val message sent to object x1
.ELAPSED ;
counter heap-new constant x2
: t3 ( u ) TIMER-RESET
0 ?DO x2 inc LOOP \ inc is an ordinary method
.ELAPSED ;
: t4 ( u ) TIMER-RESET
0 ?DO x2 [bind] counter inc LOOP \ val message sent to object x1
.ELAPSED ;
: t5 ( u ) TIMER-RESET
0 ?DO x1 [bind] xcounter val drop LOOP \ val message sent to object x1
.ELAPSED ;
Performance test of OBJECTS.FS
#8000000 t2 0.096 seconds elapsed.
#8000000 t3 0.086 seconds elapsed.
#8000000 t4 0.053 seconds elapsed.
#8000000 t5 0.048 seconds elapsed. ok
-- -----------------------------------------------------
class10.fth is a quite slow for t1b, but all the other benches are in the same
ballpark.
Is it your goal to effectively discourage use of objects.fs,
in order to stimulate growth of the NM model?
-marcel
> What is it exactly that you propose?
To adopt a standard OOP extension for Forth. In particular the Neon
Model for the reasons given in the RfD.
It would guarantee that the supporting extension would be available to
any code requiring it. A standard class library could then more
readily be added to and refined. Perhaps in a manner similar to the
FSL.
> I have no problem loading either class.fth or objects.fs in iForth.
> Both seem to be ANS Forth (or use only 'sane' extensions). I seem to
> recall that many or most Forths can load these two files correctly.
> So in fact we already have two standard ways of using objects in Forth.
Perhaps three or more if you count Bernd's BigForth ANS OOF and
anything else that someone may have put together.
> No special kernel extensions (that would need standardizing) are necessary.
True. Optimizing performance would probably need some words defined in
assembler.
> The performance of both packages is also decent:
Yes. Performance is not an issue. While not unimportant, there is a
lot more to consider than just performance.
> class10.fth is a quite slow for t1b, but all the other benches are in the same
> ballpark.
It depends on the benchmark chosen. Here's a benchmark based on
example code supplied with objects.fs. I wouldn't call either one
"slow", just different for different tests:
Performance test of OBJECTS.FS
xcounter heap-new constant x2
: testObjects ( n -- )
timer-reset
0 DO 10 x2 add LOOP
.elapsed ;
8000000 testObjects
11.7 sec
Performance test of CLASS.FTH
xcounter x2
: testNeon ( n -- )
timer-reset
0 DO 10 add: [ x2 ] LOOP
.elapsed ;
8000000 testNeon
4.62 sec
> Is it your goal to effectively discourage use of objects.fs,
> in order to stimulate growth of the NM model?
I would not discourage the use of any code by anyone.
Additionally, if someone wants to propose that objects.fs be the sole
or an alternate ANS Forth OOP extension they could certainly do that.
It's been suggested that more than one ANS object model could be made
available via a LIB-XX mechanism. While that could be done I would
prefer that a single direction be chosen rather than dilute the effort
and confuse newcomers. But that is just my preference. Ultimately it
is the ANS committee, and I presume the Forth community input, that
would decide how to handle this, if anything at all is adopted.
Regards,
-Doug
This proposal is for an optional extension. No one need use any of it,
ever, if that is their desire.
Thanks for the input.
Regards,
-Doug
> Performance test of OBJECTS.FS
> xcounter heap-new constant x2
> : testObjects ( n -- )
> timer-reset
> 0 DO 10 x2 add LOOP
> .elapsed ;
> 8000000 testObjects
> 11.7 sec
Gives me a run-time error. I have no idea why.
( add not defined for xcounter? )
-marcel
> > Performance test of OBJECTS.FS
> > xcounter heap-new constant x2
>
> > : testObjects ( n -- )
> > timer-reset
> > 0 DO 10 x2 add LOOP
> > .elapsed ;
>
> Gives me a run-time error. I have no idea why.
> ( add not defined for xcounter? )
Delete redefinition of inc in class xcounter as follows. This is what
I ran for testObjects. Sorry. I should have specified that.
Although running with the redefinition did not error for me. It did
what it was supposed to do and printed many, many lines of text.
Carbon MacForth G3 500Mhz OSX10.3.9 You didn't get a meaningful
error?
Regards,
-Doug
counter class
foobar implementation
\ m: ( object -- )
\ this [parent] inc
\ n @ 10 mod 0=
\ if
\ ." xcounter " this object-print ." made another ten" cr
\ then
\ ;m overrides inc
m: ( n object -- )
0 do
this inc
loop
;m overrides add
m: ( object -- n )
n @
;m overrides val
end-class xcounter
> Marcel Hendrix wrote:
>>> Performance test of OBJECTS.FS
>>> xcounter heap-new constant x2
>>> : testObjects ( n -- )
>>> timer-reset
>>> 0 DO 10 x2 add LOOP
>>> .elapsed ;
>> Gives me a run-time error. I have no idea why.
>> ( add not defined for xcounter? )
> Delete redefinition of inc in class xcounter as follows. This is what
> I ran for testObjects. Sorry. I should have specified that.
> Although running with the redefinition did not error for me. It did
> what it was supposed to do and printed many, many lines of text.
> Carbon MacForth G3 500Mhz OSX10.3.9 You didn't get a meaningful
> error?
Yes, sorry, I got what looked like an infinite loop and because I hadn't noticed
the redefinition of inc, nor the definition of inc in terms of add (weird idea :-)
I thought something was wrong. So no, I didn't get a crash.
> counter class
> foobar implementation
[..]
> m: ( n object -- )
> 0 do
> this inc
> loop
> ;m overrides add
Now, I tried to download class12.f, but I do not really understand how add works
there. Is it only defined for an ordered collection, not for a var?
( Up to now I have only tried the original class.f files. )
With add defined as below, objects.fs is about twice slower than class.f .
(0.4 s versus 1.0 s).
:Class CellObj <Super Object
CELL bytes Data
:M Get: ( -- n ) M@ ;M
:M Put: ( n -- ) M! ;M
:M Add: ( n -- ) 0 ?DO M@ 1+ M! LOOP ;M
:M Clear: 0 M! ;M
:M Print: M@ . ;M
\ ( ^obj -- ) copies data from another CellObj
:M ->: @ M! ;M
;Class
-marcel
> "Doug Hoffman" <dhof...@talkamerica.net> wrote Re: RfD - Object Extensions
>> Marcel Hendrix wrote:
>>>> Performance test of OBJECTS.FS
>>>> xcounter heap-new constant x2
E-mail crossed posting, sorry.
With your code I get the following result:
FORTH> in performance
Performance test of CLASS.FTH
#8000000 t1 0.065 seconds elapsed.
#8000000 t1b 0.243 seconds elapsed.
>> #8000000 t6 0.726 seconds elapsed.
Performance test of OBJECTS.FS
#8000000 t2 0.097 seconds elapsed.
#8000000 t3 0.086 seconds elapsed.
#8000000 t4 0.051 seconds elapsed.
#8000000 t5 0.048 seconds elapsed.
>> #8000000 t6 1.050 seconds elapsed. ok
So NM is about 30% faster (on iForth)
I must say I don't much like the way add is implemented :-(
-marcel
Ok, my first comment: This won't get implemented in Gforth and bigFORTH.
Main objections:
* method object syntax
* smalltalk scoping
* early binding is default
* late binding doesn't perform well
Unfortunately, in Neon, all mistakes are deeply entangled, so you can only
get rid of them by getting rid of Neon alltogether.
I'll try to find more time to port MINOS to VFX, since that's the better way
to get a saner OOP Forth extension to be used. As Stephen Pelc said: The
main vehicle to get an OOP extension pushed through is by using it (and
make other people use it), not by advocating it.
I must however admit, that my OOF does not have a string library, or the
typical container classes (lists, sets, hashes, ...). The string library I
use has about half a dozend Forth words, and that's it (and it is
completely sufficient for MINOS to be able to store and fetch strings,
insert into and append to them, and release them from the memory pool). And
the "container" classes are IMHO design patterns, not classes. Keep it
simple - simple things should not go into objects.
--
Bernd Paysan
"If you want it done right, you have to do it yourself"
http://www.jwdt.com/~paysan/
It is not a proposal that could be voted upon.
Look up Anton Ertl's proposals for the kind of detail that is
needed before a proposal can be seriously considered.
You will have to rephrase the Neon object model, and may well
consider ambiguities and design flaws doing that.
>Regards,
>
>-Doug
>
--
--
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- like all pyramid schemes -- ultimately falters.
alb...@spenarnc.xs4all.nl http://home.hccnet.nl/a.w.m.van.der.horst
\ class.fth
:class counter <super object
var n
:m val: ( -- n ) get: n ;m
:m inc: ( -- ) 1 +: n ;m
:m add: ( u -- ) +: n ;m
;class
...
\ objexamp.f
counter class
foobar implementation
m: ( n -- ) n +! ;m overrides add
m: ( -- n ) n @ ;m overrides val
end-class xcounter
I get:
Performance test of CLASS.F
#8000000 t6 0.219 seconds elapsed.
Performance test of OBJECTS.FS
#8000000 t6 0.094 seconds elapsed. ok
What's the fastest way to do it in NM?
-marcel
I agree with all except for the early binding issue. I have no problem with
early binding being the default, though late binding should also be
supported with decent performance.
Cheers,
Elizabeth
--
==================================================
Elizabeth D. Rather (US & Canada) 800-55-FORTH
FORTH Inc. +1 310-491-3356
5155 W. Rosecrans Ave. #1018 Fax: +1 310-978-9454
Hawthorne, CA 90250
http://www.forth.com
"Forth-based products and Services for real-time
applications since 1973."
==================================================
What's t6 actually measuring (you posted benchmarks up to t5, until t6
results appeared)?
I tried to measure the difference between late and early binding on bigFORTH
and an Athlon64, and got the confusing result that early binding (optional
in bigFORTH's OOF) is slightly slower than late binding. However, this is
an artificial benchmark, since the same object method is called all the
time - this is not how polymorph applications work.
Numbers (on an Athlon64 @ 2GHz): If I do a loop 8 million times, each time
doing 10 x2 add (where x2 is a pointer to an object on the heap, and
: add n +! ;
), it takes 0.060s for late binding and 0.063 for early binding. So much for
"you are taking a performance hit with late binding" ;-).
Should I create a more realistic benchmark, that avoids extremely simple to
predict pattern like the one above? The object classes in question should
have more than a few methods, since few methods are artificial, too, and
the data should consist of at least something like a list or array of
several different classes, which all have different ways to calculate
something specific.
The main difference between bigFORTH's OOF and objects.fs compiled with
iForth is probably that bigFORTH has a dedicated register for the this
pointer, and that instance variables access and method invocation happens
through well-defined macros (which are nearly optimal for their purpose -
the "n +!" doesn't get optimized to a "add [this+n],tos" instruction).
> Main objections:
>
> * method object syntax
> * smalltalk scoping
> * early binding is default
> * late binding doesn't perform well
You list your objections and that is fine. But it would be better to
also provide some information about them. Such as exactly why you
object.
1) <method object syntax> In the RfD I discuss at length this issue and
show four different ways the NM can do object method.
2) <smalltalk scoping> In the RfD I discuss why this is a better design
choice.
3) <early binding is default> Puzzling. Why is this an objection?
What problem does it cause?
4) <late binding doesn't perform well> Please define "perform well".
Also, realize that the model presented is just that, a model. There is
quite a bit that could be done in code to speed it up. I would expect
any polished final implementations to do this.
Regards,
-Doug
When I started bigFORTH's OOF (in 1992), late binding did take a (small)
performance hit, so what I did was to convert all method invocations to
early binding whenever the compiler could figure out the class of the
object in question. You can also declare selectors as "bind early", but
then, it's not just the default, it's enforced - and you can't change such
an early bind method later down the inheritance tree. If you need helper
words (factoring) inside an object, and these helper words are not part of
the interface, early binding is the only possibility, as well, and (unlike
the declared "early" methods) you can redefine these helper words in
subclasses (works like a normal Forth redefinition).
I have a convert-to-early-binding selector in bigFORTH's OOF (::), and you
can use that without preceeding class to early bind invocations of the
current class' methods. However, looking through the MINOS sources, the
only use of :: is to bind to specific scopes of something that's *not* the
current class, but a class that's close in the hierarchy. The other
definite early binding selector is SUPER (all meta-words in bigFORTH's OOF
are selectors as well).
I use :: in interactive mode inside a class definition also to "borrow"
methods from other classes, i.e.
box :: draw
is a shortcut for
: draw box :: draw ;
and leads to less code (the VT pointer of DRAW for BOX is copied into the
current VT).
This follows the "least surprise" principle. The default state of a method
is "find the closest binding", this is the most general case - if you want
to be more specific, be so. As I said, the typical case of being "more
specific" in MINOS is that you don't mean the current class.
The methods within a class hierarchy are also defined in a "general case" to
"specific case" order - the classes up in the hierarchy define general
things (like "the horizontal extension of a horizontal box is the sum of
the horizontal extensions of its content" up, and "the extension of a
button is the text string width plus some specific padding" down).
The availability of "late binding is default" leads to this programming
style. Originally, Ewald Rieger was the only person who did larger programs
with this OOF, and his programs look pretty much that way, too. And it's
not much different from real OO languages, as well.
When early binding is the default, and late binding is perceived as
"burden", you'll develop a rather different style that's missing the point.
The point of OOP is factoring on steroids, i.e. code reuse. You want to
reuse as much code as possible, and only replace that what needs to be
replaced.
Among other things, your "solutions" assume an object is simply represented
on the stack. That isn't necessarily the case. Invoking an object may
modify the search order to make its methods and sub-classes (if any) visible
(in addition to or instead of providing a token on the stack). That's very
hard to do with an inherently method-object ordering.
I also echo the complaint that to make a proposal actionable, it must
explicitly define whatever words it includes, rules of usage, and everything
else. You can't just say "like Neon" because that isn't unambiguously
defined to the extent a standard must be.
> Marcel Hendrix wrote:
>> I get:
>> Performance test of CLASS.F
>> #8000000 t6 0.219 seconds elapsed.
>> Performance test of OBJECTS.FS
>> #8000000 t6 0.094 seconds elapsed. ok
>> What's the fastest way to do it in NM?
> What's t6 actually measuring (you posted benchmarks up to t5, until t6
> results appeared)?
It is defined higher-up in the thread, but I agree it could be very
confusing. The point of t6 is to define a method called "add" in terms
of an existing method "inc". So ( n -- ) add calls inc n times.
The original class.fth by Andrew McKewan comes with var.fth, array.fth
and test.fth. These should all four be loaded. Then define two classes
counter and xcounter as follows:
--8<--------------------------------------------------
:class counter <super object
var n
:m val: ( -- n ) get: n ;m
:m inc: 1 +: n ;m
;class
:class xcounter <super counter
:m add: ( n -- ) 0 DO inc: self LOOP ;m
;class
\ then define the benchmark
xcounter x2
: t6 ( n -- )
TIMER-RESET
0 DO 10 add: [ x2 ] LOOP
.ELAPSED ;
--8<--------------------------------------------------
\ #8000000 t6 0.726 seconds elapsed.
In objexamp.fs we find the class xcounter already:
counter class
foobar implementation
m: ( object -- )
this [parent] inc
n @ 10 mod 0=
if
." xcounter " this object-print ." made another ten" cr
then
;m overrides inc
m: ( object -- n )
n @
;m overrides val
end-class xcounter
But this xcounter overrides the method inc by something unsuitable
for a benchmark, and it doesn't have an add method. So it was
modified as:
--8<-------------------------------------------------------------
counter class
foobar implementation
\ don't override inc!
\ m: ( object -- )
\ this [parent] inc
\ n @ 10 mod 0=
\ if
\ ." xcounter " this object-print ." made another ten" cr
\ then
\ ;m overrides inc
\ add add
m: ( n object -- )
0 do
this inc
loop
;m overrides add
m: ( object -- n )
n @
;m overrides val
end-class xcounter
\ now add a benchmark
xcounter heap-new constant x2
: t6 ( u ) TIMER-RESET
0 ?DO 10 x2 add LOOP
.ELAPSED ;
--8<-------------------------------------------------------------
\ #8000000 t6 1.050 seconds elapsed.
After this exercise I became curious about add being defined in
terms of inc, so I modified add as shown in the message you're
responding to, and reran the t6 benchmark shown above:
#8000000 t6 0.219 seconds elapsed. ( class.fth )
#8000000 t6 0.094 seconds elapsed. ( objects.fs)
For NM this is surprisingly slow.
> I tried to measure the difference between late and early binding on bigFORTH
> and an Athlon64, and got the confusing result that early binding (optional
> in bigFORTH's OOF) is slightly slower than late binding. However, this is
> an artificial benchmark, since the same object method is called all the
> time - this is not how polymorph applications work.
I think I can understand that, if it only happens for very basic operations
that translate to a few CPU operations (like inc or add on integers).
-marcel
Also notice the NM enforce the usage of the ':' prefix:
'Method: obj'
I think that's the best syntax for a system which uses mainly
early-binding, without modification in the core Forth.
The message (method:) is recognized easily in the source text.
The object is not an immediate word, no more un parsing word.
It can be stored and transmitted freely on the stack, like any value.
That' a normal parameter.
- The message parse the object and compiles the method in an optimal
and simple way.
>>> * smalltalk scoping
- That's an extension in the NM.
>>> * early binding is default
- That enable to not to have to optimize the implementation: I think
that Forth must remain the only language with human dimension on the
level of the implementation. Nm respects that too.
>>> * late binding doesn't perform well
>>
>> You list your objections and that is fine. But it would be better to
>> also provide some information about them. Such as exactly why you
>> object.
>>
>> 1) <method object syntax> In the RfD I discuss at length this issue
>> and
>> show four different ways the NM can do object method.
>
> Among other things, your "solutions" assume an object is simply
> represented on the stack. That isn't necessarily the case. Invoking
> an object may modify the search order to make its methods and
> sub-classes (if any) visible (in addition to or instead of providing a
> token on the stack). That's very hard to do with an inherently
> method-object ordering.
Very hard ? That's a simple list search (used also in Swoop).
: SEARCH-LIST ( msg class -- 'method T | F )
BEGIN @ DUP WHILE ( ID 'LINK )
2DUP CELL+ @ =
IF NIP 2 CELLS + TRUE EXIT THEN
REPEAT NIP ;
create object ( class) , ( data ) /allot does> cell+ ;
compile-method ( msg obj -- )
' >body cell- @ search-list
IF compile, ... THEN ...
>
> I also echo the complaint that to make a proposal actionable, it must
> explicitly define whatever words it includes, rules of usage, and
> everything else. You can't just say "like Neon" because that isn't
> unambiguously defined to the extent a standard must be.
There is a full book that describe in details the NM:
http://www.powermops.com/MopsManual/MopsManual.html
[snip]
If early-binding is the default, I think too that the NM is
the best choice.
> REFERENCE IMPLEMENTATION
>
> An ANS-compatible NM implementation, complete with full documentation
> and an example class library, is available at the following URL:
>
> http://members.talkamerica.net/douglas...@talkamerica.net/
>
> Use file CLASS12F.zip for Windows, and CLASS12MF.zip for Macintosh.
> The content is identical, but the formatting is different.
>
> Note that this implementation is based directly on the work of Andew
> McKewan circa 1996-1997.
Personally, I would have preferred to start on a simpler base:
a simplified version of the original McKewan NM.
> Respectfully submitted,
>
> Douglas B. Hoffman
> 16 Jun 2006
>
You are brave!
Charles.
Hello Charles. Thanks for your input and insight.
> Personally, I would have preferred to start on a simpler base:
> a simplified version of the original McKewan NM.
I would have no problems with this. What simplifications and form of
the original version would you recommend?
> You are brave!
I made the decision to submit the RfD while soaring in my glider the
day before. I had just made some significant, perhaps risky,
modifications to the control mixers. The modification worked
wonderfully. It was something I had been meaning to do for a long
time. Years ago I told a friend about something else that I was going
to do "some day". His response was "Doug, there is no time like the
present". He was old and had suffered several debilitating strokes.
So I knew what he meant. I took his advice and didn't regret it. He
died a couple of years later. Especially since then I try to live by
my friend's "no time like the present" advice.
Regards,
-Doug
> Among other things, your "solutions" assume an object is simply represented
> on the stack.
Yes. It is a simple concept and is one reason the NM is a simple
model.
> That isn't necessarily the case. Invoking an object may
> modify the search order to make its methods and sub-classes (if any) visible
> (in addition to or instead of providing a token on the stack). That's very
> hard to do with an inherently method-object ordering.
I'm not understanding the concern you raise here. You would like to
make an object's methods and subclasses visible? Visible in what way
beyond their current "visibility"? Is this related to the
"convenience" mechanism we were discussing where I had concerns about
"back-door" access?
> I also echo the complaint that to make a proposal actionable, it must
> explicitly define whatever words it includes, rules of usage, and everything
> else.
Yes, you are right. I plan to do all of that. But first there appear
to be some very fundamental issues to deal with. I believe the lack of
the above is not hindering this process. Also, I thank you for your
input.
Regards,
-Doug
No, I'm talking about how the "front door" works. When you select an
object, it is appropriate that you then be able to invoke its methods and/or
access its sub-classes. Search order manipulation has always been a valid
way of managing this scoping in Forth.
> >> Among other things, your "solutions" assume an object is simply
> >> represented
> >> on the stack.
> >> That isn't necessarily the case. Invoking an object may
> >> modify the search order to make its methods and sub-classes (if any)
> >> visible
> >> (in addition to or instead of providing a token on the stack). That's
> >> very
> >> hard to do with an inherently method-object ordering.
> >
> > I'm not understanding the concern you raise here. You would like to
> > make an object's methods and subclasses visible? Visible in what way
> > beyond their current "visibility"? Is this related to the
> > "convenience" mechanism we were discussing where I had concerns about
> > "back-door" access?
>
> No, I'm talking about how the "front door" works. When you select an
> object, it is appropriate that you then be able to invoke its methods and/or
> access its sub-classes.
Sorry again. I can't seem to get my head around the issue. With the
NM we can directly invoke any method of any (non-embedded) object. We
can also directly access any (non-embedded) objects that are of a class
that is a subclass of the original object and also invoke any of its
methods. This is all done directly without the need for scoping. But
I am afraid that this is not what you are talking about.
Are you perhaps referring to embedded objects?
In Mops there is the following syntax for accessing an embedded
object's (aka ivar) methods given the public object:
mymessage: IVAR> someIvar IN someObj
The enabling words are IVAR> and IN. The message mymessage: is only
valid for the embedded object someIvar. It is not a valid message for
the public object someObj. We could implement IVAR> and IN for the NM.
If we need to access deeper levels of nesting of embedded objects I'm
sure that could be done too. But the syntax might not be as clean as
what you are doing in SWOOP.
> Search order manipulation has always been a valid
> way of managing this scoping in Forth.
OK. I suspect we can achieve the same end with the NM, but by using a
different mechanism.
I hope this is the issue you wished to raise.
Regards,
-Doug
I think the syntax '[...]' to have late binding have no advantage.
Why not use only '**' ?
example:
variable x
... Get: [ x @ ] ...
x @ Get: **
I think M@ and M! are not necessary:
M@ == SELF @
M! == SELF !
( and suppress the ABORT" Class must be <General or <Indexed" )
In 'CLASS-EXT'
[ obj ref ]
M@ M!
Multiple inheritance.
Indexed primitives. ( not sure )
Cascaded message invokation.
Finally, the basic class words set is:
:Class <Super <General bytes object self super :m ;m ;Class
Regards,
Charles
>
> Bernd Paysan wrote:
>
>> Main objections:
>>
>> * method object syntax
>> * smalltalk scoping
>> * early binding is default
>> * late binding doesn't perform well
>
> You list your objections and that is fine. But it would be better to
> also provide some information about them. Such as exactly why you
> object.
>
> 1) <method object syntax> In the RfD I discuss at length this issue and
> show four different ways the NM can do object method.
That's not the problem. As Elizabeth explained, classes have its own scope,
and manipulate the vocabulary stack. That means when you do "method
object", you either have to resort to changing the parser to look one token
ahead (similar to how LSE does defining words), or you have to put all
selectors in a common scope. Putting all selectors into a common scope IMHO
is ok for small OO extensions like mini-oof or objects.fs, but a design
mistake for larger ones.
> 2) <smalltalk scoping> In the RfD I discuss why this is a better design
> choice.
Yes, that's your opinion. You are entitled to have this opinion, but my
opinion is that this is a bad design choice.
The flexibility of the Smalltalk approach might be attractive, but I don't
see why you say "the entire Forth community" should agree on a "common
class/message ordering scheme"? The selectors in sane object oriented
extensions to Forth are private to the classes defined by the individual
programmers; there is *no* conflict with other classes and messages in this
Java-like approach.
The VT approach can be extended to allow multiple inheritance (done in C++),
and to allow interfaces (done in Java), where the latter is a sort of "poor
man's multiple inheritance". I've added interfaces to my OOF, since it is
quite easy to do, but never used them in real-world applications like
MINOS.
> 3) <early binding is default> Puzzling. Why is this an objection?
> What problem does it cause?
[x] you don't really understand what OOP is about.
See my reply to Elizabeth on that topic. And the speed issue is simply not
there any more if you do it right. You say
"When defining methods of a class that call other methods of that same class
or superclass (which is simply good factoring practice), it makes a lot of
sense to use early binding."
And I say the contrary, it makes no sense. IMHO good factoring practice in
OO Forth programs is to have late binding by default, and convert to early
binding only when there's no polymorphism possible, or explicitely
requested by the programmer (and then, the scope is usually not the default
scope). That way, you can reuse higher-level code parts by redefining the
methods they invoke.
> 4) <late binding doesn't perform well> Please define "perform well".
Similar to early binding, so that you don't care about the difference
anymore. As long as you program with your mind fixed on this performance
issue, you can't leverage OO design and analysis, and if you can't do that,
what's the point of using OOP at all?
> Also, realize that the model presented is just that, a model. There is
> quite a bit that could be done in code to speed it up. I would expect
> any polished final implementations to do this.
If you are willing to waste quite some memory, you can indeed make this
model theoretically as fast as the VT approach. But that's then a
memory-speed tradeoff, and wasting memory can and will cause the speed to
go down again (limited cache sizes and such).
Where is the problem ? The NM will abort if selector SX is not in
the CX (or CX'parents) visibility.
Using a common scope enable to reuse already defined methods.
Is'nt it ? (NB: Swoop uses a common scope)
>
>> 2) <smalltalk scoping> In the RfD I discuss why this is a better
>> design
>> choice.
>
> Yes, that's your opinion. You are entitled to have this opinion, but
> my
> opinion is that this is a bad design choice.
>
> The flexibility of the Smalltalk approach might be attractive, but I
> don't
> see why you say "the entire Forth community" should agree on a "common
> class/message ordering scheme"? The selectors in sane object oriented
> extensions to Forth are private to the classes defined by the
> individual
> programmers; there is *no* conflict with other classes and messages in
> this
> Java-like approach.
Where is the problem. In the NM, the selectors are private to the
classes and parents classes. Another class cannot respond to an
undefined
method.
:class cX
:m Coucou: ." hello" cr ;m
;class
:class cY
:m Hello: ." Bye" cr ;m
;class
cX x
Hello: x \ abort - Message not understood by class
cY y
Coucou: y \ abort - Message not understood by class
>
> The VT approach can be extended to allow multiple inheritance (done in
> C++),
Yes, but it is not easy with vtables.
> and to allow interfaces (done in Java), where the latter is a sort of
> "poor
> man's multiple inheritance". I've added interfaces to my OOF, since it
> is
Very easy to have interfaces in the NM:
Selector Coucou: drop
Selector Hello: drop
:Class cA
:m test: ... Coucou: ** ... ;m
:m Coucou: ( specialize here ) ... ;m
;Class
> quite easy to do, but never used them in real-world applications like
> MINOS.
>
>> 3) <early binding is default> Puzzling. Why is this an objection?
>> What problem does it cause?
>
> [x] you don't really understand what OOP is about.
[x] Then, nobody in de C++ communauty anderstand what is OOP ?
C++ use early binding by default! (have to append the explicit 'virtual'
declaration to enable late binding) !
>
> See my reply to Elizabeth on that topic. And the speed issue is simply
> not
> there any more if you do it right. You say
>
> "When defining methods of a class that call other methods of that same
> class
> or superclass (which is simply good factoring practice), it makes a
> lot of
> sense to use early binding."
>
> And I say the contrary, it makes no sense. IMHO good factoring
> practice in
> OO Forth programs is to have late binding by default, and convert to
> early
> binding only when there's no polymorphism possible, or explicitely
> requested by the programmer (and then, the scope is usually not the
> default
> scope). That way, you can reuse higher-level code parts by redefining
> the
> methods they invoke.
By experience, I'm sure that it's perfectly viable to have
early binding by default and to explicitly manage late binding.
When I create a class, I know perfectly the methods which will establish
the polymorphism.
Furthermore, the resulting code will be more efficace.
>
>> 4) <late binding doesn't perform well> Please define "perform well".
>
> Similar to early binding, so that you don't care about the difference
> anymore. As long as you program with your mind fixed on this
> performance
> issue, you can't leverage OO design and analysis, and if you can't do
> that,
> what's the point of using OOP at all?
>
>> Also, realize that the model presented is just that, a model. There
>> is
>> quite a bit that could be done in code to speed it up. I would
>> expect
>> any polished final implementations to do this.
>
> If you are willing to waste quite some memory, you can indeed make
> this
> model theoretically as fast as the VT approach. But that's then a
> memory-speed tradeoff, and wasting memory can and will cause the speed
> to
> go down again (limited cache sizes and such).
I have implemented a NM that use virtuals tables. There is no conflict.
Pure vtable will use a lot of memory (sometimes only for one more
method).
Imagine a class with 1000 methods. If you specialize the class with
one method (perhaps a method that need only early binding), you have to
copy the entire table in the new class.
>
> --
> Bernd Paysan
> "If you want it done right, you have to do it yourself"
> http://www.jwdt.com/~paysan/
Regards,
Charles Mélice
> >> Personally, I would have preferred to start on a simpler base:
> >> a simplified version of the original McKewan NM.
> I think the syntax '[...]' to have late binding have no advantage.
> Why not use only '**' ?
> example:
>
> variable x
> ... Get: [ x @ ] ...
>
> x @ Get: **
>
> I think M@ and M! are not necessary:
>
> M@ == SELF @
> M! == SELF !
>
> ( and suppress the ABORT" Class must be <General or <Indexed" )
>
> In 'CLASS-EXT'
> [ obj ref ]
> M@ M!
> Multiple inheritance.
> Indexed primitives. ( not sure )
> Cascaded message invokation.
>
> Finally, the basic class words set is:
>
> :Class <Super <General bytes object self super :m ;m ;Class
I like the idea of the very simple, yet powerful, basic word set.
I gather that in order to create an object in the heap one would first
need a named dictionary object. We then send the Heap: message to that
"prototype" object?
Regards,
-Doug
Here we are going to disagree. I think Charles Melice answered this
well.
> > 2) <smalltalk scoping> In the RfD I discuss why this is a better design
> > choice.
>
> Yes, that's your opinion. You are entitled to have this opinion, but my
> opinion is that this is a bad design choice.
>
> The flexibility of the Smalltalk approach might be attractive, but I don't
> see why you say "the entire Forth community" should agree on a "common
> class/message ordering scheme"?
Perhaps we are having a mis-communication of what it means to be
Smalltalk-like and Java-like for this issue. I am using the
definitions laid out by Anton in his "On standardizing Object-Oriented
Forth Extensions" article. Quoting just a few sentences from that
article:
"The 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." .... "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".
> > 4) <late binding doesn't perform well> Please define "perform well".
>
> Similar to early binding, so that you don't care about the difference
> anymore. As long as you program with your mind fixed on this performance
> issue, you can't leverage OO design and analysis, and if you can't do that,
> what's the point of using OOP at all?
I have experimented with your ANS compatible OOF package. The
performance, while not bad, was on par or a bit slower than the Neon
model and Anton's objects.fs. So I am puzzled by this. Is it a
requirement to make changes in the kernel or to write OOF in assembler
to realize the speed gains you speak of?
> > Also, realize that the model presented is just that, a model. There is
> > quite a bit that could be done in code to speed it up. I would expect
> > any polished final implementations to do this.
>
> If you are willing to waste quite some memory, you can indeed make this
> model theoretically as fast as the VT approach. But that's then a
> memory-speed tradeoff, and wasting memory can and will cause the speed to
> go down again (limited cache sizes and such).
I have observed that simply defining a few critical words in assembler
can at least double the speed of the NM late binding. If someone
wishes to make it even faster, apparently they can do so using some or
all of the techniques you describe. That would be their decision.
Either way, the NM late binding can be made to be very fast.
Thank you for your input.
Regards,
-Doug
The other way round will abort compilation when the selector is not visible.
I prefer that. Oh, I realize, due to NM's early-binding-by-default you
usually get the error on compilation, as well. That's fixing one problem
with another.
> Using a common scope enable to reuse already defined methods.
> Is'nt it ? (NB: Swoop uses a common scope)
Yes, making bad practice possible is something you can do with this
"feature" ;-). Ok, when I look at the Win32Forth sources, I can see a
pattern: make bad practice happen more often ;-).
>> The VT approach can be extended to allow multiple inheritance (done in
>> C++),
>
> Yes, but it is not easy with vtables.
Agreed, but I think MI is an abomination, anyway ;-).
>> [x] you don't really understand what OOP is about.
>
> [x] Then, nobody in de C++ communauty anderstand what is OOP ?
>
> C++ use early binding by default! (have to append the explicit 'virtual'
> declaration to enable late binding) !
Come on, C++ allows to declare early-by-default and late-by-default
selectors. That's fine for me, it allows me to choose what I want. My OOF
lets me choose what I want, as well (and I did explain that here). NM
allows me only to declare early-by-default, and explicitely select late
binding on invocation.
> By experience, I'm sure that it's perfectly viable to have
> early binding by default and to explicitly manage late binding.
My experience is simply different. I tell you: Nothing in the MINOS sources
(about 500k) backs your claim.
> When I create a class, I know perfectly the methods which will establish
> the polymorphism.
Yep, fine - so declare those that establish the polymorphism late-binding-by
default, don't you? That's what I do.
> Furthermore, the resulting code will be more efficace.
Nope. Haven't you seen my benchmark results where bigFORTH's OOF early
binding was slightly slower on my Athlon64? The difference is between
call addr (early binding)
and
mov edx,[op]
call method[edx] (late binding)
and apparently the branch target buffer of the Athlon64 makes the latter
faster than the further.
> Imagine a class with 1000 methods. If you specialize the class with
> one method (perhaps a method that need only early binding), you have to
> copy the entire table in the new class.
Yes, but a class with 1000 methods is clearly a case of "mad programmer
disease", and a specialization which needs only early binding is even
worse. I can't imagine why anybody sane would do that. Good style is when a
class has between 5 and 30 late binding methods; sometimes classes may have
two purposes (like the DISPLAY class in MINOS, which can be both a widget
and a drawing display), and then, up to 60 methods would be sane.
Or use a good optimizing compiler like iForth or VFX. Actually, to really
make OOF fast, you need a dedicated register for the current object, but
that should apply to all these models as well. And VFX's way of doing
inlining conflicts with the way my OOF creates methods (not via CREATE
DOES>, but via : and postpone), so the benefit is not that much.
The ANS Forth version defines words for the methods (late binding) and
compiles early bindings directly into the code, so I expect it to be
somewhat slower on late bindings than on early bindings (a constant factor,
though).
Since you are a PowerPC-Mac user, I can't just direct you to bigFORTH to
compare an optimized version of my OOF with your favourite optimized
version of NM.
How many DEFERs do you use in non OO Forth ? Probably just a few, the
ones needed, no more. Thus we already have the capacity to detect the
location where late-binding is appropriate.
I can admit that default late-binding is a confort. But that's a
costly confort. The cost is the vtable usage. Without default
late-binding, we can avoid vtables.
I forget to say. When I speak about comfort, I evoke only the heat
kicked away by the processor occupied with making indirections where
the programmer knew where to fix his definitions.
You like sculpturing in the plasticine. I prefer to fix my ideas
in the stone.
Charles
> Actually, to really
> make OOF fast, you need a dedicated register for the current object, but
> that should apply to all these models as well.
OK. Unfortunately, I don't think that route will be viable as a
specification for ANS extensions. If only we could.
Regards,
-Doug
I use quite a few, and also use replacible vectors (some sort of specialized
mini-oof without inheritance) in places.
But when I program OO, I don't program classic Forth. I program something
where you stick together objects, and the sticking makes the system work.
In MINOS, there's only one class that doesn't need a lot of late-binding
methods: the 3D turtle. Well, it needs the ordinary set of a widget, but
the additional interface (for the 3D turtle itself) never gets subclassed,
so all of those methods are early bound.
> I can admit that default late-binding is a confort. But that's a
> costly confort. The cost is the vtable usage. Without default
> late-binding, we can avoid vtables.
But without late-binding, what we do IMHO is to avoid OO. There's nothing
wrong with avoiding OO, but if you define an OO extension, avoiding OO is
sort of pointless ;-).
The starting point of my OOF was Dick Pountain's OOF. A bigFORTH user had
ported this over to bigFORTH (Ewald Rieger), and complained that it needs
some improvements. I then looked at it and rewrote something similar in
syntax, but with VT based late binding.
> But without late-binding, what we do IMHO is to avoid OO. There's nothing
> wrong with avoiding OO, but if you define an OO extension, avoiding OO is
> sort of pointless ;-).
I don't see where anyone has said let's not do late binding with an ANS
standard. The NM does late binding just fine.
I think the root problem is we can't get the late-binding speed of OOF
because we can't have an ANS extension dedicate a register. OOF then
becomes noticeably slower than the NM according to tests I've run.
In my use of the NM I often see where dictating a late bind would be
useful while creating a class so I go ahead and do it. In those cases
where I don't realize it until later, when designing a subclass, it is
a simple matter to go back and change the superclass method to use a
late bind. It's really no big deal at all to do it that way. The best
part is we can have an ANS standard object extension with overall
excellent perormance, late binding and all.
Regards,
-Doug
We can define a set of primitives needed for fast OOP extensions.
Things like
o# ( #u -- addr ) generates an instance variable address
o#exec ( i*x #u -- j*x ) execute method with id u of the current object
(index into vtable)
>o ( addr -- r:addr' ) set o to addr, push old content of o on return stack
o> ( r:addr' -- ) pop content of return stack to o
o! ( addr -- ) set o to addr.
o could be named 'a' as well, since then, you can define the MISC
instructions like A!+ and A@+. The "#" notation of the stack picture means
"from the instruction stream".
You can define these primitives in terms of ANS Forth, and the vendors than
can feel free to provide faster versions if they like to.
It will be interesting to read your RfD.
Regards,
-Doug
If we have a standard for the OO extension, the implementor is free to
choose an implementation strategy that makes it fast. We might put in a
proposal for primitives that allows people to create various fast OO
extensions on top of it, as well.
There's only one difficulty here: The technique to make VTs fast is obvious
and easy to describe. The possible techniques to make Smalltalk-like scoped
late binding fast are not obvious, since a number of tradeoffs can be made:
* You could implement this scheme with a VT approach, given that you can
expand all the VTs of all classes and active objects around when a new
selector is defined, or you preallocate something you find reasonable, and
let the user choose to change the amount of preallocated table if that's
not enough.
* You could implement it with a hash table, i.e. part of the ID is the index
into this table, and then you search for the rest of the ID there. This
takes less space and more time than the VT approach
* You could "degenerate" this hash-table version into a single linked list
with id+xt pairs to search for.
* Instead of using linked lists, binary trees could be used.
and an ample amount of other possible options, including reverse mapping
(i.e. for every selector, there's a set of (class,xt) tuples).
the NM system would also require
o+! ( offset -- ) add the offset to the current value of o
since it's needed for embedded objects.Also would >o and o> be better
expressed as using an object stack (which may or may not be the return
stack) to accomodate architectures where a separate object stack makes
more sense (I don't know of one off-hand but I'm sure there either is
one or will be in future), with restrictions on use to accomodate using
the return stack as well.
George Hubert
My OOF can also make use of this instruction.
> Also would >o and o> be better
> expressed as using an object stack (which may or may not be the return
> stack) to accomodate architectures where a separate object stack makes
> more sense (I don't know of one off-hand but I'm sure there either is
> one or will be in future), with restrictions on use to accomodate using
> the return stack as well.
Yes, that makes sense.
Not very much. I doubt that any system will use a separate stack, so
this will result in a situation where a standard program cannot use
the knowledge that these cell-sized values are on the return stack
even though all standard systems implement it that way.
You might want to propose the thing with longer and more
self-explanatory names, however. These are not going to be words that
are used in a lot of places.
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2006: http://www.complang.tuwien.ac.at/anton/euroforth2006/
Actually, those primitives that take arguments from the instruction stream
don't need to be visible at all - you have to provide a word which compiles
the primitive + the data. And the rest (like >o and o>) also won't be used
directly by the user of the OO extension.
There is one exception; generally most OO extensions expose a word to
fetch the current object, however there's no consensus on the name (NM
and other smalltalk like systems tend to use SELF while java type
systems favour THIS) so OO extensions would alias the word anyway (and
in some cases place it in other than the Forth wordlist; Win32Forth
places SELF in the classes Vocabulary which is always inherited by the
embedded vocabulary of classes so it's normally only visible when
defining a class (or prototype object with :object .... ;object). In
fact (for ITC) >o and o> are incorporated into the method entry and
exit codes (which are in assembler) and not needed by high-level forth
at all. In the STC version being developed they'll be macros (compiling
a 5byte call for 3byte push or pop is overkill) which will be executed
in the appropriate place (at compile time).
Isn't the point to allow Forth systems to expose a small wordset which
can be integrated with other features such as CATCH and THROW (perhaps
we should add that where the extensions are available then the
information to unwind the object stack is saved and restored as part of
the exception handling) and multi-tasking (either by using a register
which is saved/restored by context switches or using a user variable)
as well as allowing systems that have a free register to make it easily
available for more optimal code.
For older systems a generic harness (using variable or value) could be
supplied as a reference implementation and then users/maintainers could
use any ANSI (Plus OO implementation words) compatible OO extension
with any ANSI Forth system (with a more optimal system-specific harness
if necessary) they choose.
George Hubert
Another way is to standardize a wordset and a specification
that fit to nearly all OO systems.
That will break all actual models, but at least enable any
system to fit to the new model:
For instance, the lookup operator '>>' is the OBJ-METHOD,
METHOD-OBJ solver. '>>' is immediate, parse, decode...
or do nothing !
To resolve NM "method: obj" construct:
>> obj Method \ early binding
To resolve immediate parser object:
>> obj .. \ leave obj address on the stack
Resolve NM { Method [ ] } construct.
>> Method \ late binding
>> Class Method \ class-proxy early binding
Charles
One of the reasons that Win32Forth stuck with the method object syntax
was because although Tom Zimmer and Andrew McKewan felt (with
hindsight) object method would have been better there was too much
existing code (both in the W32F system itself; somebody would have to
rewrite it, and published code on the web) to change it. The same still
applies; if a re-write was done then it would probably resemble
something else (e.g SWOOP) or even switch to a Vtable approach like
Gforth/bigForth-minos. I would imagine the same applies to MOPS as
well. I don't think an acceptable common object system is possible, but
a wordset covering support for any object system (with a reference
system for those w/o support; which can be adapted to provide a harness
for older (no longer maintained) systems with partial object support)
would enable the provision of alternate systems as libraries, with
class libraries for those and users could choose their preferred
system.
George Hubert
> One of the reasons that Win32Forth stuck with the method object syntax
> was because although Tom Zimmer and Andrew McKewan felt (with
> hindsight) object method would have been better there was too much
> existing code (both in the W32F system itself; somebody would have to
> rewrite it, and published code on the web) to change it. The same still
> applies; if a re-write was done then it would probably resemble
> something else (e.g SWOOP) or even switch to a Vtable approach like
> Gforth/bigForth-minos. I would imagine the same applies to MOPS as
> well. I don't think an acceptable common object system is possible, but
> a wordset covering support for any object system (with a reference
> system for those w/o support; which can be adapted to provide a harness
> for older (no longer maintained) systems with partial object support)
> would enable the provision of alternate systems as libraries, with
> class libraries for those and users could choose their preferred
> system.
I agree that we are not going to be able to come up with a standard
that meshes perfectly with all existing code. Even for Win32Forth and
PowerMops. However, one great advantage of using the extremely
*similar* Neon model proposed (similar to current Win32Forth and
PowerMops) is that there are already a large number of users that will
be very comfortable with reading and using the proposed ANS-compliant
object code. Detail differences will exist. Such as CLASSINIT:
behavior. But as long as these differences are known then there should
be no problem. One other very great advantage of having an ANS
standard in and of itself is that newcomers to Forth will have an
officially supported objects package. There will be no need to scour
the web and try to find one on their own (of course they could always
do this anyway, but I believe there are clear advantages to ANS
standards).
Btw there are Forth users, including myself, that *prefer*
method-object. Even if we were to do a complete rewrite of everything
starting today.
Regards,
-Doug
What Charles Melice proposed ( the >> operator preceeding all
object/method pairs ) would have been object method anyway (though it
could simply resolve the 2 arguments quite easily provided there's no
naming conflict, and IMO having an object and a method with the same
name is plain stupid). I agree that having an agreed standard for the
NM model makes sense, and equally for other popular object systems that
have ANSI versions as well, then they can all be available as
libraries. What is needed IMO is low level support for common features
(i.e. the object stack) so systems can provide optimum support and
integrate objects with other features such as multi-tasking.
George Hubert
>...
> One of the reasons that Win32Forth stuck with the method object syntax
> was because although Tom Zimmer and Andrew McKewan felt (with
> hindsight) object method would have been better there was too much
> existing code (both in the W32F system itself; somebody would have to
> rewrite it, and published code on the web) to change it. The same still
> applies; if a re-write was done then it would probably resemble
> something else (e.g SWOOP) or even switch to a Vtable approach like
> Gforth/bigForth-minos. I would imagine the same applies to MOPS as
> well.
No, it doesn't. (Not that I think name-dropping is particularly
productive...)
FWIW, Mops (or more specifically PowerMops) now does have vtable
binding if you use a reference to access an object. It has its
place, but isn't the same as fully late-binding, in which you can
compile a message send to an object even whose base class hasn't
even been declared yet.
I know that many say that object-method is less elegant, what with
colons and brackets and words like ivar> class_as> etc etc,
but just speaking personally, I find it makes the code more
readable since it's obvious what everything means, and what's
going to happen when it runs.
But everyone has their favorite syntax and so maybe we'll never
agree (it's a bit like Pascal vs C etc). I think the low-level
wordset idea sounds interesting.
> I don't think an acceptable common object system is possible, but
> a wordset covering support for any object system (with a reference
> system for those w/o support; which can be adapted to provide a harness
> for older (no longer maintained) systems with partial object support)
> would enable the provision of alternate systems as libraries, with
> class libraries for those and users could choose their preferred
> system.
Yes, exactly.
Cheers, Mike.
----------------------------------------------------------------
Mike Hore mike_h...@OVE.invalid.aapt.net.au
----------------------------------------------------------------
--
Posted via a free Usenet account from http://www.teranews.com
> ...
> I know that many say that object-method is less elegant
>...
Of course I meant method-object, but you all knew that :-)