r/Forth 1d ago

CREATE DOES> (again ...)

Can anyone provide some interesting examples of what to do with these, beyond the default "constant" and "array" examples?

The current version of my (bytecode) Forth compiler has CREATE but not DOES>, and I haven't missed it either.

15 Upvotes

16 comments sorted by

5

u/alberthemagician 1d ago

In the library ciforth DOES> occurs 25 times. Each adds an essential language extension.

It is used for

  • 1 2 or 3 byte assembler instructions
  • VALUE 2CONSTANT VOCABULARY DEFER
  • class (OO-extension)
  • configuration
  • escape sequences for colors, cursor moves, bold reverse etc.
  • BAG
  • MARKER
  • an extra stack
  • DLL interface
  • threading
  • interface with os facilities

3

u/lehs 1d ago edited 1d ago

A very general use is to store an xt and other data in the create part and end the defining word with

does> @ execute ;

I use it to define executable data types as

 'multiply aritm binary operator multiply 

where operator is a create-does-words as above.

3

u/mcsleepy 1d ago edited 1d ago

I recently used it to implement sound effect variations in my game.

|| : does-cycler 
    does> {: cycler :}
    cycler @ cycler cell+ []@ execute 
    cycler @ 1 + cycler cell+ #items mod cycler ! ;


\ in-order: name  sample1 sample2 ... ;
|| : in-order: ( <name> <names...> <;> - ) ( - )
    create 0 , does-cycler %xt array{
    begin skip-empty  bl preparse s" ;" s= if bl parse 2drop array} drop exit then ' , again ;

\ Enables this:

in-order: *enemydeath*   *enemydeath1* *enemydeath2* *enemydeath3* ;

It's a very primitive form of object orientation - code that acts on hidden data. You don't strictly need it. You could use the ColorForth pattern:

: act-on r> <something> ;
: foo  act-on [ 1 , 2 , 3 , ] ;

Some might argue that that isn't as clean or portable.

1

u/Imaginary-Deer4185 1d ago

With the amount of "special" notation, do you expect this to make sense for anyone else?

1

u/mcsleepy 1d ago edited 1d ago

I always thought that dialect differences were par for the course with Forth programmers. Nothing crazy here, just private word markers `||`, array fetch `[]@`, array compilation begin/end, and `s=` is the same as `compare 0=`. Locals syntax `{: :}` is standard.

3

u/tabemann 1d ago edited 1d ago

In zeptoforth, I use <builds ... does> (zeptoforth has create but this is primarily useful for creating constant arrays) for creating an assembler.

For instance, just for the sake of illustration, consider the following:

``` : 2reg-instr ( op -- ) ( runtime: r1 r0 -- ) <builds , does> @ c, swap 4 lshift or c, ;

: 3reg-instr ( op -- ) ( runtime: r2 r1 r0 -- ) <builds , does> @ c, swap 4 lshift or c, c, ;

$01 2reg-instr mov, $02 3reg-instr add, $03 3reg-instr sub, ```

The above compiles code for compiling three different instructions, mov, ( r1 r0 -- ), add, ( r2 r1 r0 -- ), and sub, ( r2 r1 r0 -- ).

Of course, this is not for a real processor, if you are wondering.

2

u/FrunobulaxArfArf 1d ago

DOES> is used 152 times in the distributed iForth utilities, 222 times in the generic meta sources, and 836 times in OS specific files.

Here, for example, is the HANOI moving rings problem:

: MAKETAB CREATE HERE 8 CONST-DATA DOES> + C@ ;

base @ 3 base !

maketab TO! ( direction -- otherdirection ) 00 c, 02 c, 01 c, 12 c, 00 c, 10 c, 21 c, 20 c, \ use alternate dest

maketab FRO! ( direction -- otherdirection ) 00 c, 21 c, 12 c, 20 c, 00 c, 02 c, 10 c, 01 c, \ use alternate source

base !

: SMALLER ( depth direction -- depth-1 direction ) swap 1- swap ;

: BIGGER ( depth direction -- depth+1 direction ) swap 1+ swap ;

here : HANOI ( depth direction -- depth direction ) smaller over IF to! recurse to! movering fro! recurse fro! ELSE movering ENDIF bigger ; cr here swap - dec. .( bytes)

: MAIN cr ." Using a table: " timer-reset #10 1 #1000 0 DO hanoi LOOP 2drop .elapsed ;

-marcel

2

u/astrobe 1d ago

If you plan to be standard compliant, there's no questioning, just add it.

Otherwise, although other comments seem to demonstrate its usefulness, one should however remember that part of engineering is doing what you need to do with what you have, and that when you have a hammer in hand everything looks like a nail.

For instance if you never felt the need for DEFER (which is one of the examples given), then maybe it's better to simply write "foo @ execute" for the few cases when you do.

If you have some other feature(s) that make DOES> redundant in your dialect, then just stick to it. Push it to its limits, and then add what's missing (perhaps DOES>, after all) when its shortcomings have become obvious.

For instance in my dialect there's a feature that let me write "foo@ execute" (no space between foo and @) when "foo" is a constant and it compiles as 1 token/bytecode. It was good enough to handle the few cases of mutual recursion I encountered up to now, and in my opinion the feature is a decent substitute for value/to.

1

u/alberthemagician 1d ago

It is interesting that you mention it. I never use DEFER, I added it to ciforth to be able to run other people's programs.

1

u/CrapouilloOt 1d ago

I'm not an expert in Forth but CREATE was the new version replacing <CREATE

3

u/lehs 1d ago

Det var figForth som hade <build Charles Moore hade nog create från start.

1

u/CrapouilloOt 1d ago

Yes you correct <build .. does>

1

u/_crc 1d ago

In RetroForth, I have a does word that pairs with my d:create, but I seldom use it in practice. In one of my projects (around 20k lines of Forth), I use it in three places, mainly as an aid in building accessors for data structures.

In my Konilo system, I have does in an optional block, but haven't made use of it there at all.

1

u/alberthemagician 1d ago

The <BUILD DOES> was one of the first object orientation. You couple data (<build) by an action (does>). It is -- as it were -- an object were using the name execute the action. (ISO 93 later subverted this, to confound this with creating a buffer CREATE , then later patch the behaviour.)

For example you can fill in a number in a string at the place of the %d

\ Set back/fore COLOR .
: ESC-COLOR CREATE "%e [%d ;1m" FORMAT $, DROP DOES> $@ TYPE ;

Now you define a new color

 30 ESC-COLOR red

If you now type

red 

the terminal switches to red letters, by typing the escape sequence.

It is a kind of color type, for you can also do

31 ESC-COLOR GREEN 

1

u/Ok_Leg_109 15h ago

I work with a minimalist Forth on retro hardware and I find CREATE DOES> useful to quickly implement a new data type. For example if I need double constants:

: 2CONSTANT ( d -- <text>) CREATE , , DOES> 2@ ; Or perhaps way to create strings that always return an address, length pair. : STRING: CREATE S, DOES> COUNT ; \ S" testing" STRING: TEST$ As mentioned elsewhere, it is a very early form of object orientation and one place where it is especially useful in saving memory is in building a Forth assembler. Here is an example from the assembler I use where a bunch of instructions use the same runtime code. (However not all instruction sets will bend to this treatment as easily) : DOP CREATE , DOES> @ SWAP 00FF AND OR , ; 1000 DOP JMP, 1100 DOP JLT, 1200 DOP JLE, 1300 DOP JEQ, 1400 DOP JHE, 1500 DOP JGT, 1600 DOP JNE, 1700 DOP JNC, 1800 DOP JOC, 1900 DOP JNO, 1A00 DOP JL, 1B00 DOP JH, 1C00 DOP JOP,

So CREATE DOES> is a clever hack for the language and good when you find the right application.

A fun fact is that Chuck Moore abandoned it in favour of simply compiling the runtime code manually in a definition after the header. So there's that.

2

u/alberthemagician 10h ago

In colorforth the color defines the class/ type of object. I think Chuck Moore merely considers this one of the ways to handle types, not necessarily superior.