Functions in Forth
Learn how to define and use functions in Forth - word definitions, stack effects, parameters, recursion, locals, and higher-order words with Docker-ready examples
In most languages, a “function” has a name, a parenthesized parameter list, and a return statement. Forth has none of these. Instead, Forth functions are called words, and they communicate exclusively through the data stack. A word pops its inputs off the stack, does its work, and pushes its results back. There are no named parameters and no explicit return statement — what’s left on the stack when the word finishes is the return value.
This is the heart of Forth’s concatenative paradigm: programs are built by composing small words, each of which transforms the stack. Because every word reads from and writes to the same shared stack, a definition like : CUBE DUP DUP * * ; reads almost like a pipeline. The trade-off is that you must keep the stack’s shape in your head, which is why every well-written Forth word carries a stack effect comment such as ( n -- n³ ).
In this tutorial you’ll learn how to define words with : and ;, how to pass multiple inputs and return multiple outputs through the stack, how to write recursive words with RECURSE, how to use named local variables when the stack juggling gets awkward, and how to treat words as values with execution tokens — Forth’s take on higher-order functions.
Defining and Calling Words
A word definition starts with : (colon), followed by the word’s name, its body, and a closing ; (semicolon). The stack effect comment ( before -- after ) documents what the word consumes and produces — it’s a comment, but an essential one.
Create a file named functions.fth:
| |
SQUARE uses DUP to duplicate the top value (so 5 becomes 5 5) and then * to multiply them, leaving 25. The word . (dot) prints and removes the top of the stack. GREET takes no input and leaves nothing behind — its only effect is printing.
Multiple Inputs and Multiple Returns
Because results live on the stack, a Forth word can return several values just as easily as one — it simply leaves more than one item behind. Here a word consumes two numbers and returns both their sum and their product.
Create a file named multi_return.fth:
| |
This relies on stack-manipulation words: 2DUP copies the top two items (a b → a b a b), + adds the top pair into sum, and -ROT rotates the top three items so the original a b move back to the top (a b sum → sum a b) where * can multiply them. The two results, sum and prod, are simply left on the stack for the caller to use.
Recursion with RECURSE
A word’s name isn’t visible inside its own definition (it isn’t fully defined until the closing ;), so Forth provides RECURSE to call the word currently being defined. The classic example is factorial.
Create a file named recursion.fth:
| |
For 5, the word checks DUP 1 > (is 5 greater than 1?), and since it is, it computes 5 * FACTORIAL(4). The recursion unwinds down to FACTORIAL(1), where 1 > 1 is false, so the ELSE branch runs DROP 1, returning 1. Multiplying back up the chain gives 120.
Local Variables for Readability
Deep stack juggling can become hard to read. Gforth supports named locals using the Forth-2012 {: ... :} notation. The names are bound from the stack in left-to-right order matching the stack effect — the leftmost name takes the deepest value, the rightmost takes the top.
Create a file named locals.fth:
| |
Without locals, this word would need several DUP, SWAP, and ROT operations to reuse x three times. The {: a b c x :} line names the inputs once, so the body reads almost like ordinary algebra. Locals are scoped to the word — they exist only while QUADRATIC runs and are invisible elsewhere. For storage that persists across calls, you’d use a global VARIABLE or CONSTANT instead.
Higher-Order Words with Execution Tokens
Forth words are values too. The ' (tick) word returns the execution token (xt) of the word that follows it, and EXECUTE runs the xt currently on top of the stack. Together they let you pass behavior into a word — Forth’s equivalent of a function argument.
Create a file named higher_order.fth:
| |
' INC pushes the execution token for INC onto the stack. Inside TWICE, we keep a second copy of that token safe on the return stack with >R / R> while the first EXECUTE runs INC on 5 (giving 6), then apply it again to get 7. Passing a different word — say ' SQUARE — would change the behavior without touching TWICE at all.
Running with Docker
| |
Each file already ends with bye, so the -e bye is a harmless safeguard that guarantees Gforth exits after running.
Expected Output
Running functions.fth:
25
Hello from a Forth word!
Running multi_return.fth:
Product = 12
Sum = 7
Running recursion.fth:
120
Running locals.fth:
45
Running higher_order.fth:
7
Note the trailing space after each number — the . word always prints a number followed by a single space.
Key Concepts
- Words are functions — Defined with
: name ... ;, a word has no parameter list and noreturn; it communicates only through the shared data stack. - Stack effect comments are documentation — Always annotate definitions with
( before -- after ); they’re the contract that keeps stack code readable. - Multiple return values are free — A word returns as many results as it leaves on the stack, so
( a b -- sum prod )is just as natural as a single return. RECURSEcalls the current word — Since a word’s name isn’t bound until its closing;, recursion usesRECURSErather than the word’s own name.- Locals tame the stack — Gforth’s
{: a b :}notation binds inputs to names scoped to the word, replacing long chains ofDUP/SWAP/ROTwhen clarity matters. - The return stack is for temporary stashing —
>RandR>move items aside so the data stack stays clean; always pair them within the same word. - Words are first-class via execution tokens —
'fetches a word’s xt andEXECUTEruns it, enabling higher-order words that take behavior as an argument.
Running Today
All examples can be run using Docker:
docker pull forthy42/gforth:latest
Comments
Loading comments...
Leave a Comment