by kragen | 2025-12-16 16:15:00+00:00
Forth "words" are subroutines. C "functions" are also subroutines. Forth "words" are "not functions" merely because the use of "function" for "subroutine" is terminology specific to certain families of languages (C, Lisp) to which Forth does not belong. This is all irrelevant because I didn't say anything about "functions", just dataflow. My example expression is just as valid in Perl (which calls subroutines "subroutines") or Ruby (which calls them "methods").

A colon definition containing five sequential calls to different words without any control flow or stack manipulation is perfectly unremarkable. Here's a sample word from the F83 block editor, which I am using because F83 is generally accepted as highly competent Forth code, if not exemplary:

n NEW moves the terminal's cursor to the start of line n, and overwrites lines until a line is begun with null input ( a Carraige Return). : NEW (S n -- ) L/SCR SWAP DO [ FORTH ] I [ EDITOR ] T EDIT-AT >IN OFF QUERY SPAN @ IF P ELSE [ FORTH ] I REDISPLAY LEAVE THEN .SCREEN LOOP .SCREEN ;

The phrase t edit-at >in off query span is just such a sequence, six words long rather than 5. The word off here is a standard word that sets a memory location to 0, and >in is the standard input-pointer variable, although you can't really be sure of that without more context—note, for example, that the editor vocabulary has redefined i as an insertion command rather than the usual loop-counter definition, thus the vocabulary switching commands.

The dataflow in that six-word code sequence is, in C syntax,

t(i); edit_at(); off(&in); query(); &span ...

But to figure that out, I had to look up the stack comments of t, edit-at, query, and span (which is just a variable), and know the stack effects of >in and off. (In the Forth-83 standard https://www.complang.tuwien.ac.at/forth/fth83std/FORTH83.TXT query was the usual way to do what we do nowadays with accept—read a line of input, implicitly into tib.)

With slightly different definitions, this could easily have been, for example,

edit_at(t(i)); off(&in); span(query()) ...

Reconstructing the dataflow thus is not some kind of insuperable difficulty. It took me a while in this example, but were I more expert with Forth, and in particular Forth-83, I probably could have figured it out relatively quickly. If it were taking too much time, or if the stack comments were wrong, I could have figured it out interactively at the REPL, or single-stepping through the code in F83's debugger.

My point is just that reconstructing the dataflow is a problem you have to solve when you are reading Forth code. The author knew what the dataflow was, presuming it's working code you're looking at. An efficient compiler would have to know, too. But, as the reader, you don't know until you reconstruct it mentally with your global knowledge of the things it's calling. It's an extra error-prone decoding step between the code you're looking at and the semantic understanding you seek.

(I said "without stack manipulation", but stack manipulation generally makes the problem more mentally challenging, not less.)

By contrast, with pop infix syntax, the dataflow is represented locally with the parentheses and commas.

The same thing is true of types in languages with implicit typing (like OCaml and JS), of when call/return pairs follow a conventional stack discipline in Scheme (where the possibility of call/cc exists), of variable capture and escape in languages with closures, of which variables are mutated in languages like Python where all variables are mutable (and to a slightly smaller extent in languages like C, where immutability is possible but not default). The author has this information; the maintainer needs it to successfully modify the program; the compiler would need it to avoid producing grievously inefficient code; and there's no way to express it in the language.

My claim is that, while the optimum may be subjective on this spectrum between implicitness and explicitness, the implicitness itself is objective. You claim to disagree, but to me it sounds like you disagreed simply because you didn't understand what I was trying to express.