Let me introduce you to one final, fairly general and widely used MUF facility before letting you go.
Evaluation stacks are very efficient ways of allocating and freeing small amounts of temporary storage, but most languages use them very clumsily, a problem which often becomes evident when dealing with functions which accept or return a variable number of arguments.
Traditional Forth implementations (and tinyMuck MUF) are little better, using a clumsy and error-prone convention of an integer count pushed on top of the argument vector.
Muq MUF systematizes this by introducing the concept of stack blocks and a variety of operations upon them.
A stack block may be created by enclosing some number of arguments
between [
and |
. The |
is syntactic sugar for a count
which tracks the size of the stackblock and allows operations to
be performed upon the block quite efficiently:
root: [ "this" "is" "a" "block" "of" "words" | root: [ "this" "is" "a" "block" "of" "words" | |sort root: [ "a" "block" "is" "of" "this" "words" | |mix root: [ "block" "this" "is" "of" "words" "a" | |pop root: [ "block" "this" "is" "of" "words" | "a" --> _tmp root: [ "block" "this" "is" "of" "words" | _tmp root: [ "block" "this" "is" "of" "words" | "a" |push root: [ "block" "this" "is" "of" "words" "a" | |shift root: [ "this" "is" "of" "words" "a" | "block" |unshift root: [ "block" "this" "is" "of" "words" "a" | ]vec root: #<vec> vals[ root: [ "block" "this" "is" "of" "words" "a" | |dup[ root: [ "block" "this" "is" "of" "words" "a" | [ "block" "this" "is" "of" "words" "a" | ]|join root: [ "block" "this" "is" "of" "words" "a" "block" "this" "is" "of" "words" "a" | |sort root: [ "a" "a" "block" "block" "is" "is" "of" "of" "this" "this" "words" "words" | |uniq root: [ "a" "block" "is" "of" "this" "words" | |length root: [ "a" "block" "is" "of" "this" "words" | 6 pop root: [ "a" "block" "is" "of" "this" "words" | |dup[ root: [ "a" "block" "is" "of" "this" "words" | [ "a" "block" "is" "of" "this" "words" | ]pop root: [ "a" "block" "is" "of" "this" "words" | 1 |rotate root: [ "block" "is" "of" "this" "words" "a" | -1 |rotate root: [ "a" "block" "is" "of" "this" "words" | 3 |rotate root: [ "of" "this" "words" "a" "block" "is" | ]words root: "of this words a block is" words[ root: [ "of" "this" "words" "a" "block" "is" | ]join root: "ofthiswordsablockis" "i" chopString[ root: [ "ofth" "swordsablock" "s" | ]words root: "ofth swordsablock s" vals[ root: [ 'o' 'f' 't' 'h' ' ' 's' 'w' 'o' 'r' 'd' 's' 'a' 'b' 'l' 'o' 'c' 'k' ' ' 's' | |upcase root: [ 'O' 'F' 'T' 'H' ' ' 'S' 'W' 'O' 'R' 'D' 'S' 'A' 'B' 'L' 'O' 'C' 'K' ' ' 'S' | |downcase root: [ 'o' 'f' 't' 'h' ' ' 's' 'w' 'o' 'r' 'd' 's' 'a' 'b' 'l' 'o' 'c' 'k' ' ' 's' | |sort root: [ ' ' ' ' 'a' 'b' 'c' 'd' 'f' 'h' 'k' 'l' 'o' 'o' 'o' 'r' 's' 's' 's' 't' 'w' | |uniq root: [ ' ' 'a' 'b' 'c' 'd' 'f' 'h' 'k' 'l' 'o' 'r' 's' 't' 'w' | |charInt root: [ 32 97 98 99 100 102 104 107 108 111 114 115 116 119 | |sum root: [ 32 97 98 99 100 102 104 107 108 111 114 115 116 119 | 1422 pop root: [ 32 97 98 99 100 102 104 107 108 111 114 115 116 119 | |intChar root: [ ' ' 'a' 'b' 'c' 'd' 'f' 'h' 'k' 'l' 'o' 'r' 's' 't' 'w' | |for v do{ v , } "\n" , abcdfhklorstw root: [ ' ' 'a' 'b' 'c' 'd' 'f' 'h' 'k' 'l' 'o' 'r' 's' 't' 'w' |
The general convention is that a function with a name ending with
[
creates a stackblock (think of Unix shell syntax '<' for
opening a pipeline), a function beginning with |
operates on a
pre-existing stackblock (again, think of Unix shell pipe notation) and
a function beginning with ]
consumes a stackblock (like Unix
shell >
for terminating a pipeline).
Obviously, every [
in an expression should have a balancing
]
somewhere.
See the block section of the MUF reference manual for a fuller listing of the block functions.
MUF functions may actually accept and return multiple
stackblocks, as well as multiple individual arguments. The syntax for declaring
this to the compiler uses []
in place of the $
used for
declaring a scalar argument or result:
root: : ]]pop { [] [] -> } ]pop ]pop ; root: [ "abc" | [ "def" | root: [ "abc" | [ "def" | ]]pop root:
Block arguments must always precede (be below) scalar arguments, both call and return.
Using stack blocks is a great way to avoid grinding the garbage collector heavily by creating scads of small objects as intermediate values in an expression. For example, manipulating a stackblock of characters generates no garbage, while doing the equivalent operation with string operators might generate a number of garbage strings. (Note, however, that strings shorter than eight characters also count as zero garbage, since they are stored as immediate stack values rather than being allocated on the heap.)
Go to the first, previous, next, last section, table of contents.