Forth lecture 15.

Structures and classes in Forth.

The disadvantages of the old stuff.

BUILDS DOES was one of the first object oriented stuff ever (pre 1975).

It has it inside out.

: CONSTANT CREATE , DOES> @ ;

CREATE makes a header, then its data is filled, then it is bestowed with some action.

Fundamental problems:

All structs defined in Forth copy object oriented concepts in an unForthy way, because they make a distinction between data (fields) and methods (code). But of course a field is merely a method that puts an address on the stack. We will see that it is the default action, comparable to a

 DOES>
that is followed immediately by a semicolon.

The correct approach.

If we turn

: CONSTANT CREATE , DOES> @ ;

inside out we get the more correct


0 \ Sample input.
CLASS CONSTANT
M: CONTENT @ M;   ,
ENDCLASS

This generalizes to

0 0 \ real, imaginary
class COMPLEX
M: REAL-PART @ M;        ,
M: IMAGINARY-PART @ M;   ,
endclass

This is in contrast with the CREATE DOES construct that doesn't bear generalizing. It is also in contrast with the oo en vogue, in that there is no distinction between data and methods. A smart field is just some action working on an offset in a data structure.

The usage of COMPLEX is

1 2 COMPLEX Z
Z REAL-PART .   Z IMAGINARY-PART .

So Z makes this the current object (by filling in a pointer ^COMPLEX) and REAL-PART adds a known offset to this pointer, then does its thing. The second Z can be left out.

The definition of Z requires some class dependent data, in this case 1 and 2. The data to build the data structure is to be supplied during the definition of the class too. This is such that a trial build can take place and the offsets of the fields can be determined.

At first sight it may seem that the requirement to make objects current is AK ward, but in practice it isn't too bad. I can testify that this is a viable and productive way to handle structures. It was very successful in my rewrite of the manx package. It was also instrumental in my ciasdis project.

At first I used still the old syntax as follows

struct aap
:F REAL-PART , DOES> @ ;
:F IMAGINARY-PART , DOES> @ ;
endstruct

It may be less neat but functionally equivalent, but the interesting point is that structures in Forth automatically become classes.

BUILDS DOES> had the useful property that because there was just one method there was no need to specify the smart field.

In order to have this we could have a M:DEFAULT

This generalizes to

CLASS COMPLEX
M:DEFAULT 2@ ;        2,
ENDCLASS

Usage

COMPLEX Z Z . .

The default field is automatically executed when the object is used. This makes it however impossible to not have that action, if you only need some other method of the class. I am somehow convinced that using colors in color Forth works out very pleasantly, because the same word can have more than one action depending on its color.

Implementation notes.

In the implementation I have currently, a dummy object of the class is build once during compilation, and during that building the offsets are collected. This means that words like the comma in the above example are executed as well as compiled. For this purpose some code is temporarily stored in strings. This sound horrendous. However the whole class facility costs two screens, not bad for ISO Forth. For reference they follow in the next section.

Reference ISO implementation

This is the implementation of class endclass. It is still mimics the CREATE DOES< construction. My ministring package is of the essential.

( M: auxiliary_for_class ) \ AH A4nov26
WANT SWAP-DP
CREATE NAME$ 128  ALLOT         \ The name of the struct.
CREATE CRS$ 4096 ALLOT          \ Evaluate buffer, general.
VARIABLE LAST-IN                \ Start of interpreted code
VARIABLE DP-MARKER              \ Start of alternative dict.
: !IN   IN @ LAST-IN ! ;        \ Remember last value of ``IN''
: IN$  LAST-IN @ IN @  OVER - ; \ Return input STRING since !IN
: itoa   0 <# #S #> ;           \ Transform an INT to a STRING.
\ Compile "method" working on addres with offset.
    CREATE M$ 256 ALLOT         \ Evaluate buffer, method.
: M:   IN$ 3 - CRS$ $+!   "^" M$ $!   NAME$ $@ M$ $+!
   " @ " M$ $+!   HERE DP-MARKER @ - itoa M$ $+!   " +" M$ $+!
   SWAP-DP   :   M$ $@ EVALUATE ;
\ End compiling a method.
: M;   POSTPONE ;   SWAP-DP   !IN ; IMMEDIATE
( class endclass ) \ AH A4jun16
WANT M:
: +NAME    NAME$ $@ CRS$ $+! ;  \ Add the name.
: +NAME+$   +NAME   CRS$ $+! ;  \ Add the name and a STRING.

\ Define class "name". Compile this-pointer, start build-word.
: class   NAME NAME$ $!   "VARIABLE ^" CRS$ $!
   +NAME   CRS$ $@ EVALUATE   ": BUILD-" CRS$ $!
   " HERE >R " +NAME+$   SWAP-DP   HERE DP-MARKER !   !IN ;

\ Recover memory, compile build-word and class-word.
: endclass   ?EXEC   DP-MARKER @ HERE - ALLOT   SWAP-DP
   IN$ 9 - CRS$ $+!   " R> ;" CRS$ $+!   CRS$ $@ EVALUATE
   ": " CRS$ $!   " CREATE BUILD-" +NAME+$   " ^" +NAME+$
   " ! DOES> ^" +NAME+$   " ! ;" +NAME+$   CRS$ $@ EVALUATE ;


For colorforth we would have to use two new colors probably. A new color is needed be for defining a class, the red, yellow and green colors are for defining, execution and compilation of fields and a separate color again for the default action.

This is how it would look:

<orange>COMPLEX   ( a class)
<red>REAL-PART <yellow>@        <green>,
<red>IMAGINARY-PART <yellow>@   <green>,

The red color is used with a class name to define a word from that class. ( How to end a class is yet unclear or maybe we need a ; here.)

Washer example

The washer example is famous for it supposedly demonstrates that good Forth code needs no comment. It show how to program a washing machine. This is an object oriented version of the code for lina or wina version 4.x.x. For newer versions of ciforth you may need to preceed it with
   WANT -legacy-

  • Other Forth lectures
  • Go to the home page of Albert van der Horst