Using Rexx Stem Variables to Build and Iterate Indexed Collections
Learn how to use Rexx stem variables as indexed lists: set a stem default, store the count in tail 0, iterate with a DO loop, and avoid common pitfalls like unset tails and stem‑wide drops.
01 May 2026, 04:43 UTC

Quick answer: store a list in a Rexx stem
In Rexx there are no built‑in arrays or hash tables. The language’s core data structure is the stem (also called a compound variable). By convention you keep the number of elements in tail 0 and store the actual values in tails 1 through n. This pattern works in classic Rexx, Open Object Rexx (ooRexx), Regina Rexx, and mainframe TSO/E Rexx.
Worked example: a simple list of strings
The following five‑line script creates a list, assigns two values, and prints them in order. Run it in any Rexx interpreter that supports the classic stem syntax (e.g., rexx on Linux, ooRexx, or Regina). No special privileges are needed; just read/execute permission on the script file.
/* stemlist.rex – demonstrate a Rexx stem as an indexed list */
/* 1. Set a default for the whole stem (optional but idiomatic) */
MYSTEM. = 'UNSET'
/* 2. Store the count in tail 0 */
MYSTEM.0 = 2
/* 3. Assign the elements */
MYSTEM.1 = 'Alpha'
MYSTEM.2 = 'Beta'
/* 4. Iterate using the count */
DO i = 1 TO MYSTEM.0
SAY MYSTEM.i
END
/* 5. Show what an unassigned tail returns */
SAY 'Unassigned tail 99 ->' MYSTEM.99
When you run the script (rexx stemlist.rex or ooRexx stemlist.rex) you should see:
Alpha
Beta
Unassigned tail 99 -> MYSTEM.99
The loop works because the tail i is substituted before the variable lookup: MYSTEM.i becomes MYSTEM.1, then MYSTEM.2, etc. The default MYSTEM. = 'UNSET' ensures that any tail you have not explicitly set evaluates to the string UNSET instead of its own name.
How the stem/tail mechanism works
- Stem: the part before the first period (e.g.,
MYSTEM.). It is fixed for the lifetime of the variable. - Tail: everything after the first period. The interpreter treats the tail as a plain string, upper‑cases it, and substitutes any simple variable names it contains.
- Default assignment:
STEM. = valueassignsvalueto every possible compound variable with that stem until you override a specific tail. - Unassigned tails: if you reference a tail that has neither a default nor an explicit assignment, Rexx returns the tail’s own name in upper case (e.g.,
STEM.99yields'STEM.99'). This is why checking with the stem default or theSYMBOL()built‑in is necessary.
Limits and common mistakes
Understanding where the stem model diverges from traditional arrays helps avoid subtle bugs.
- Tail expressions are not allowed
You cannot write
MYSTEM.(i+1)orMYSTEM.i+1. The tail must be a simple variable or a literal string. Compute the index into a temporary variable first:j = i + 1 SAY MYSTEM.j - Dropping a stem vs. dropping a single tail
DROP MYSTEM.removes the default and *all* compound variables that share the stem, effectively clearing the whole collection.DROP MYSTEM.3only removes tail 3; the rest of the list remains intact. This differs from array‑like “delete element” operations and can leave gaps in your logical sequence. - Unset tails masquerading as valid data
If you forget to initialize the stem default and compare an unassigned tail to a string, the comparison may succeed unexpectedly:
/* MYSTEM. not set */ IF MYSTEM.5 = 'YES' THEN SAY 'surprise match'Here
MYSTEM.5evaluates to'MYSTEM.5', so the test fails; but if you compared against'MYSTEM.5'you would get a false positive. Guard withIF SYMBOL('MYSTEM.5') = 'VAR' THEN ...or rely on a stem default. - Case‑insensitivity of tails
Because tails are upper‑cased,
mySystem.nameandMYSTEM.NAMErefer to the same variable. Mixed‑case naming does not create separate entries.
Practical verification steps
To confirm that your stem‑based list behaves as expected on your platform:
- Create a test script (like the one above) named
stemtest.rex. - Run it with two different interpreters if available, e.g.:
rexx stemtest.rex(Regina)ooRexx stemtest.rex(Open Object Rexx)
- Check that the output matches the expected sequence and that the unassigned tail prints its own upper‑cased name.
- Optional: add a line
SAY SYMBOL('MYSTEM.99')to see that the symbol returnsLIT(literal) for an unset tail.
Risk note: Changing a stem default (STEM. = newValue) overwrites *all* currently unset tails. If you rely on the default as a sentinel value, be sure to re‑assign it after bulk updates or avoid using the default for data that must persist.
When to prefer stem‑based collections
Stems are ideal for scripts that need simple, ordered lists or associative‑style maps where the key is a short, uppercase string (e.g., configuration parameters, CSV fields, or records read from a file). They require no external libraries and work unchanged across the major Rexx implementations, making them a portable choice for automation on Linux, Windows, z/OS, and other platforms where Rexx is available.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.