So, I was trying to figure out how to manage global vs. local state, and made the dumbest program possible:
/** array.rexx */
a. = 0
a.0 = 10
DO i = 1 TO a.0; a.i = i; END
SAY array_string("a.") -- prints 1 to 10
SAY alen -- prints "ALEN" because it doesn't exist in global scope
EXIT 0
/* Returns string representation of an array, stem. passed as first arg. */
array_string:
PARSE ARG _arrayvar .
RETURN _array_string()
_array_string: PROCEDURE EXPOSE (_arrayvar)
alen = VALUE(_arrayvar || 0)
str = ""
DO i = 1 TO alen
IF i > 1 THEN str = str || ", "
str = str || VALUE(_arrayvar || i)
END
RETURN str
So… I've made a global variable "a." full of numbers. PROCEDURE makes a local variable context until you hit RETURN; you should never work at global scope if you can avoid it. So to pass a. in, I have to bounce through a global function array_string(), set a global var _arrayvar to whatever arg was given, which calls a local function _array_string and the paren in EXPOSE (_arrayvar)
expands that into "a.", and then use VALUE() to dynamically read the variable.
Well, it works. I remember now how crazy complex REXX programs got, with giant global databases being exposed in every function. I bet this is the origin of my "World object" pattern in so many later languages.
This and another dumb example or two ate a couple hours of my brain, but it kinda makes sense again, you know? Interesting for a language I haven't written in anger in 20 years. I went fully back to SHOUTING KEYWORDS, it just makes sense.
The Python version of this is:
#!/usr/bin/env python3
def array_string(arrayvar):
return ", ".join(map(str, arrayvar))
a = []
for i in range(1, 11): a.append(i)
print(array_string(a))
Which I'd argue is just as stupid, because Python thinks ranges end 1 before the last number, doesn't know how to join numbers with strings, on a large array that map will consume all memory, and is a single-pass interpreter so I have to put the function above the script code; but it is much, much shorter.