You can see my original posts about it on comp.lang.forth.
\ FunForth: Functional Forth \ Haskell inspired data types and pattern matching. \ \ Disclaimer: This code doesn't pretend to be \ pretty, clever, useful or documented. \ memory allocation -- allocate, memory shouldn't leak : reserve ( n -- a ) cells allocate drop ; : release ( a -- ) free drop ; : @# ( a n -- x1 .. xn ) tuck 1- cells + swap 0 do dup @ swap [ 1 cells ] literal - loop drop ; : !# ( x1 .. xn a n -- ) 0 do tuck ! cell+ loop drop ; : constructor: ( arity "name" -- ) create , does> dup @ if dup @ 1+ dup reserve dup >r swap !# r> then ; : deconstruct ( ref -- x1 .. xn ) dup >r dup cell+ swap @ @ @# r> release ; : (||0) ( constructor -- ) s" literal over = if drop" evaluate ; : (||) ( constructor -- ) s" literal over @ = if deconstruct" evaluate ; : || ( "constructor" -- ) ' >body dup @ if (||) exit then (||0) ; immediate : ;; ( -- ) s" exit then" evaluate ; immediate
For an example of how to use it see FunForthLists.
A data type is defined by a number of constructors. Here is the definition of a linked list. The number passed to 'constructor:' is the arity, the number of arguments the constructor takes.
0 constructor: nil ( -- list ) 2 constructor: cons ( list1 x -- list2 )
We can create a list using the constructors:
nil 3 cons 2 cons 1 cons
When the constructor is applied it allocates memory, moves the arguments to that memory and leaves a pointer to it on the stack.
Words which act on data types follow the same pattern; an action is supplied for each constructor. Actions have the form
|| <constructor> .. ;;
Here's a disection of 'lmap'.
: lmap ( xt list1 -- list2 ) || nil ( xt -- nil ) drop nil ;; || cons ( xt list1 x -- list2 ) -rot over swap 2>r execute 2r> recurse swap cons ;; ;
The data item we're applying this word to has to be on top of the stack. Note that the original data item is consumed; the result is a completely new list.
: lmap ( xt list1 -- list2 )
The action for 'nil' is pretty simple; applying the xt to an empty list gives us an empty list. We drop the xt and return 'nil'.
|| nil ( xt -- nil ) drop nil ;;
Upon entry to an action, the data item is deconstructed (contents placed on the stack) and it's memory released.
|| cons ( xt list1 x -- list2 )
Shuffle the stack and store away the tail and a copy of the xt for later recursion.
-rot over swap 2>r
Apply the xt to the head.
Apply the xt to the tail.
Construct the list result and return it.
swap cons ;;
End of lmap.