Last updated: July 2016
FMS - Forth Meets Smalltalk
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:
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.
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.
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.
Named or nameless objects can be passed on the stack, placed in locals, and manipulated in any way desired.
All message sends are by default dynamically resolved to the proper method at run time.
Message polymorphism is freely available with no requirements that the classes be related.
Class variables are supported.
Introspection is supported.
Support for optional region based memory is built in (a simple form of garbage collection).
Instance variable names are private to their class and any subclasses, as in Smalltalk.
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.
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.
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 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
point origin \ instantiate a dictionary point object named originorigin 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
=> Point at X0 Y0 \ demonstrates that show: is late-bound in class point
point ul \ use a previously defined class to create instance variables
: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
=> 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
:m x: ." x: from foo " ;m
:m y: ." y: from foo " self x: ;m
=> 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
mybar y: => y: from bar y: from foo x: from bar \ note that “x: from bar” is invoked by “self x:” in class foo