Beginner

Variables and Types in Forth

Learn about stack values, variables, constants, and value storage in Forth with practical Docker-ready examples

Most programming languages start with variable declarations and type systems. Forth takes a radically different approach — it is typeless. There are no integers, floats, or strings in the traditional sense. Everything operates on the data stack, and the programmer is responsible for interpreting what the values mean.

This doesn’t mean Forth lacks ways to store and name data. Forth provides several mechanisms for named storage: VARIABLE, CONSTANT, and VALUE. Each serves a different purpose and interacts with the stack in its own way. Understanding these mechanisms is essential because they form the foundation for building more complex Forth programs.

In this tutorial, you’ll learn how Forth handles data on the stack, how to create named storage with VARIABLE, CONSTANT, and VALUE, and how to work with different kinds of data including integers, characters, and strings.

Stack Values: Forth’s Primary “Variables”

In Forth, the data stack itself is the primary place where values live. When you type a number, it gets pushed onto the stack. When you call a word like +, it pops values from the stack and pushes the result back. Most Forth code passes data entirely through the stack without ever naming it.

Stack manipulation words let you rearrange values:

WordStack EffectDescription
DUP( n -- n n )Duplicate top of stack
DROP( n -- )Discard top of stack
SWAP( a b -- b a )Swap top two items
OVER( a b -- a b a )Copy second item to top
ROT( a b c -- b c a )Rotate third item to top
NIP( a b -- b )Drop second item
TUCK( a b -- b a b )Copy top item below second

This stack-centric approach means many Forth programs use far fewer named variables than equivalent programs in other languages. The stack is the variable space.

Named Storage: VARIABLE, CONSTANT, and VALUE

When stack juggling becomes unwieldy, Forth provides three ways to create named storage.

VARIABLE

A VARIABLE allocates a memory cell and gives it a name. You store values with ! (store) and retrieve them with @ (fetch):

1
2
3
4
5
VARIABLE counter       \ allocate a cell named 'counter'
0 counter !            \ store 0 into counter
counter @ .            \ fetch and print: 0
5 counter !            \ store 5
counter @              \ fetch: now 5 is on the stack

The name counter pushes the address of the cell onto the stack, not its value. You must explicitly fetch (@) to get the value and store (!) to set it.

CONSTANT

A CONSTANT stores a value that never changes. Unlike a variable, using the name pushes the value directly onto the stack — no @ needed:

1
2
42 CONSTANT answer     \ define a constant
answer .               \ prints 42 directly — no @ needed

VALUE

A VALUE is a hybrid — it pushes its value like a constant, but you can change it with TO:

1
2
3
4
10 VALUE score         \ define a value
score .                \ prints 10 — no @ needed
25 TO score            \ update the value
score .                \ prints 25

VALUE is often preferred over VARIABLE for simple named storage because it avoids the @/! ceremony.

Comprehensive Example

Create a file named variables.fth:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
\ Variables and Types in Forth
\ Demonstrating VARIABLE, CONSTANT, and VALUE

\ --- Constants ---
42 CONSTANT ANSWER
100 CONSTANT MAX-SCORE
65 CONSTANT LETTER-A

\ --- Variables ---
VARIABLE count
VARIABLE total

\ --- Values ---
0 VALUE current-score
0 VALUE player-name-len

\ Initialize variables
0 count !
0 total !

\ --- Demonstrating CONSTANT ---
: show-constants ( -- )
    ." === Constants ===" CR
    ." The answer is: " ANSWER . CR
    ." Max score: " MAX-SCORE . CR
    ." Letter A ASCII: " LETTER-A . CR
;

\ --- Demonstrating VARIABLE with @ and ! ---
: show-variables ( -- )
    ." === Variables ===" CR
    5 count !
    ." Count is: " count @ . CR
    count @ 10 * total !
    ." Total is: " total @ . CR
    \ Increment count
    1 count +!
    ." Count after +1: " count @ . CR
;

\ --- Demonstrating VALUE with TO ---
: show-values ( -- )
    ." === Values ===" CR
    75 TO current-score
    ." Current score: " current-score . CR
    current-score 10 + TO current-score
    ." After adding 10: " current-score . CR
;

\ --- Stack as variables ---
: show-stack-math ( -- )
    ." === Stack Math ===" CR
    ." 3 4 + = " 3 4 + . CR
    ." 10 3 * = " 10 3 * . CR
    ." 7 DUP * = " 7 DUP * . CR
;

\ --- Characters and ASCII ---
: show-characters ( -- )
    ." === Characters ===" CR
    ." Letter A: " LETTER-A EMIT CR
    ." Letter B: " LETTER-A 1+ EMIT CR
    ." Letter C: " LETTER-A 2 + EMIT CR
;

\ Run all demonstrations
show-constants
show-variables
show-values
show-stack-math
show-characters

bye

Cell Size and Numeric Ranges

Forth’s cell size depends on the implementation. In Gforth on a 64-bit system, a cell is 8 bytes (64 bits). You can query this at runtime.

Create a file named variables_types.fth:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
\ Forth Data Sizes and Numeric Representations

: show-cell-info ( -- )
    ." === Cell Size ===" CR
    1 CELLS ." Bytes per cell: " . CR
    CR
;

\ --- Signed and unsigned interpretation ---
: show-numeric ( -- )
    ." === Numeric Values ===" CR
    ." Signed 42: " 42 . CR
    ." Signed -1: " -1 . CR
    ." Unsigned -1: " -1 U. CR
    CR
;

\ --- Double-cell (2x precision) numbers ---
\ In Gforth, a double-cell number uses two stack cells
: show-double ( -- )
    ." === Double-Cell Numbers ===" CR
    ." Double 1000000: " 1000000. D. CR
    ." Double 123456789: " 123456789. D. CR
    CR
;

\ --- Boolean values ---
\ In Forth, 0 is false, non-zero (typically -1) is true
: show-booleans ( -- )
    ." === Boolean Values ===" CR
    ." TRUE = " TRUE . CR
    ." FALSE = " FALSE . CR
    ." 5 > 3 = " 5 3 > . CR
    ." 2 > 7 = " 2 7 > . CR
    CR
;

\ --- Character values ---
VARIABLE my-char
: show-chars ( -- )
    ." === Character Storage ===" CR
    CHAR A my-char !
    ." Stored char: " my-char @ EMIT CR
    CHAR Z my-char !
    ." Changed to: " my-char @ EMIT CR
    CR
;

show-cell-info
show-numeric
show-double
show-booleans
show-chars

bye

Running with Docker

1
2
3
4
5
6
7
8
# Pull the Gforth image
docker pull forthy42/gforth:latest

# Run the variables example
docker run --rm -v $(pwd):/app -w /app forthy42/gforth:latest gforth variables.fth -e bye

# Run the data types example
docker run --rm -v $(pwd):/app -w /app forthy42/gforth:latest gforth variables_types.fth -e bye

Expected Output

Output from variables.fth:

=== Constants ===
The answer is: 42
Max score: 100
Letter A ASCII: 65
=== Variables ===
Count is: 5
Total is: 50
Count after +1: 6
=== Values ===
Current score: 75
After adding 10: 85
=== Stack Math ===
3 4 + = 7
10 3 * = 30
7 DUP * = 49
=== Characters ===
Letter A: A
Letter B: B
Letter C: C

Output from variables_types.fth:

=== Cell Size ===
Bytes per cell: 8
=== Numeric Values ===
Signed 42: 42
Signed -1: -1
Unsigned -1: 18446744073709551615
=== Double-Cell Numbers ===
Double 1000000: 1000000
Double 123456789: 123456789
=== Boolean Values ===
TRUE = -1
FALSE = 0
5 > 3 = -1
2 > 7 = 0
=== Character Storage ===
Stored char: A
Changed to: Z

Key Concepts

  • Forth is typeless — all stack cells hold integers, and the programmer decides how to interpret them (signed, unsigned, character, address, boolean)
  • The stack is the primary data space — most Forth code passes values through the stack without naming them at all
  • VARIABLE allocates a memory cell — use ! to store and @ to fetch; the name pushes the cell’s address, not its value
  • CONSTANT defines an immutable value — using the name pushes the value directly onto the stack
  • VALUE combines convenience with mutability — pushes its value like a constant, but can be changed with TO
  • +! increments a variable in place — a common shortcut for DUP @ n + SWAP !
  • Boolean values are integersFALSE is 0 and TRUE is -1 (all bits set), and comparison words return these values
  • Cell size is implementation-dependent — Gforth on 64-bit systems uses 8-byte cells, giving a range of roughly ±9.2 quintillion for signed values

Running Today

All examples can be run using Docker:

docker pull forthy42/gforth:latest
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining