Last updated: July 2016



FMS - Forth Meets Smalltalk




Contact


Doug Hoffman



Downloads




GENERAL COMMENTS


The FMS object programming extension is class-based and uses object-message syntax. The important characteristics of FMS are listed below, in no particular order:

  1. Smalltalk-like duck typing. Any message can be sent to any object. If the message is not valid for that object then a “not understood” error occurs.

  2. Creating a message name, creating the method code, binding that message name to that code, and over riding superclass methods if necessary are all done in one step as in Smalltalk.

  3. Declaring instance includes the ability to use a class (embedded-object-as-instance-variable). This is not the same as storing an object in a container (which can also be done) and has advantages.

  4. Named or nameless objects can be passed on the stack, placed in locals, and manipulated in any way desired.

  5. All message sends are by default dynamically resolved to the proper method at run time.

  6. Message polymorphism is freely available with no requirements that the classes be related.

  7. Class variables are supported.

  8. Introspection is supported.

  9. Support for optional region based memory is built in (a simple form of garbage collection).

  10. Instance variable names are private to their class and any subclasses, as in Smalltalk.



FMS


The FMS code has been tested on several commonly used ANS compatible Forths.

A primary design goal of FMS is to provide a simple but programmer friendly ANS Forth compatible object programming environment. The syntax for defining new classes and methods is straightforward and clear, but provides excellent performance and capability.

Documentation and an example class library is provided which should provide instruction on how to use FMS.



Example


The following FMS code example should give you an idea of what programming in FMS is like.

\ Define a new class, named point. The default superclass is class OBJECT.

:class point 
  cell bytes x  \ define an instance variable(ivar), named x, using the bytes primitive
  cell bytes y
  :m show:     \ define a message name, show:, and associate that name with the following method code
        x @ .  \ an ivar address is obtained by executing its name
        y @ . 
        ;m
  :m dot: ." Point at "  self show: ;m  \ a late bound send of show: to self
  :m init: 0 x ! 0 y ! ;m  \ init: will be implicitly called at object instantiation time
;class

point origin  \ instantiate a dictionary point object named origin
origin dot:   \ send a message to the object
=>   Point at 0 0

:class label-point <super point \ the superclass is class point
  :m show: ." X" x @ .  ." Y" y @ .  ;m
;class

label-point p1

p1 dot:
=> Point at X0 Y0 \ demonstrates that show: is late-bound in class point

:class rectangle 
  point ul  \ use a previously defined class to create instance variables
  point lr
  :m init: ul init: lr init: ;m  \ initialize the two embedded objects
  :m show: ul dot:  lr dot: ;m   \ send messages to the instance variables
  :m dot: ." Rectangle, "  self show: ;m 
;class

rectangle r

r dot:
=> Rectangle, Point at 0 0 Point at 0 0


\ Dynamically allocate objects in the heap.
\ Once created, their use is identical to dictionary objects.

heap> point  \ the nameless object is placed on the stack

dup dot:  \ message sends are the same
=>  Point at 0 0     

<free  \ free the heap memory


\ Accessing overridden methods using SUPER  

:class foo 
  :m x:  ." x: from foo  " ;m
  :m y:  ." y: from foo  " self x: ;m  
;class

foo myfoo
myfoo y:
=>  y: from foo  x: from foo 

:class bar <super foo
  :m x:  ." x: from bar  " ;m
  :m y:  ." y: from bar  " super y: ;m  \ access overridden y: method using super
;class

bar mybar
mybar y: =>  y: from bar  y: from foo  x: from bar  \ note that “x: from bar” is invoked by “self x:” in class foo