A Forth Apologia (1988)
holonforth.com
holonforth.com
The language itself leaves a lot to be desired, in my opinion, but the core idea of having a small extensible compiler around which one can build efficient problem specific DSLs is brilliant. Unfortunately I think it is too easy to get bogged down in the bad parts (or at least parts that appear bad to our modern programming sensibilities). For a good overview of what makes Forth special, I recommend Thinking in Forth https://thinking-forth.sourceforge.net even if you never expect to program in Forth. It was written in the 80s but it still holds up. Read the first 80 pages or so carefully and I can almost promise that it will change the way you think about programming in a good way.
Once you have internalized the Forth mindset, you don't need to actually write Forth code. But you may be grateful that Forth taught you how to find your optimal programming language that exists somewhere inside of yourself.
https://lists.sr.ht/~vdupras/duskos-discuss/%3C69cfdb97-49d6...
i will take a look
well, that's unfair, because https://github.com/kragen/stoneknifeforth definitely doesn't qualify as a forth, it doesn't have immediate words
(i'd say 'leads' but there don't seem to be a lot of people doing things in forth these days; duncan's 01988 estimate of 50000 'serious' forth programmers seems two orders of magnitude higher than seems likely today)
i think the appeal is that in 16k or 64k you get a repl which (unixlike) doubles as a scriptable ui, a sort of source-level debugging, interactive variable inspection (and i/o port poking, memory dumping, etc.), instant turnaround for changes (no 'my code's compiling'), multithreading, virtual memory, an assembler and disassembler, an extensible compiler with a turing-complete macro system, and dramatically better code density than machine code
all of that makes a pretty appealing package, especially for certain kinds of tasks, and it's only loosely coupled to the language qua language
as duncan points out, part of the appeal is that the development environment is a lot more integrated than something like turbo pascal
The biggest problem for me was the amount of effort needed to keep track of "local" variables.
Was I missing something? Is there a way to let Forth keep track of where on the stack "foo" lives if it is needed several times in a subroutine instead of always shuffling the stack or counting the elements on it?
I'm not a Forth novice, much less an expert, but my impression is that the answer is that if you spend much time thinking about the location of variables, then you're "doing it wrong"—to steal the phrase, trying to write C in Forth, rather than to write Forth. My understanding is that the emphasis is on building highly domain-specific languages out of many small words, each of which is re-factored so much that it never needs to keep much track of any, or much, shuffling of variables on the stack.
But combinator languages are pretty hard to read no matter what one does, don't they?
forth is ruthlessly imperative
c:
x = a[n-1];
n += y * x;
plot(x, y, n);
forth: : foo n @ 1- a @ x ! ; \ ‡
: bar y @ x @ * n +! ;
: baz x @ y @ n @ plot ;
: quux foo bar baz ;
naming each line of code allows you to do things like bar n ?
to see what n is after you barin exchange for the somewhat rebarbative syntax and lack of typing you get turing-complete macros (with full compiler access, without the pitfalls of string-replacement macros) and an interactive repl that fits into almost the smallest microcontrollers
because you have total freedom to redefine the syntax you can construct whatever dsl you prefer
it is nothing like combinatory logic except that you can occasionally write a point-free function
______
‡ this assumes a is defined as something like this
: array create cells allot
does> swap cells + ;
20 array a
so that 3 a computes an &a[3] suitable for @ and !, etc.yes
use variable or value for variables, not the stack
the stack is for expression evaluation
roughly everyone has this problem at first
if you'd put it in a local variable in c or js, 85% of the time you should put it in a variable in forth. start with 100% or 110% and scale back from there
yes this means your words aren't reentrant by default
this is ok, most subroutines aren't recursive
never shuffle the stack or count the elements in it. if you find yourself doing that, put the elements you wanted to access in a variable or value
then you can access them by name. this is much easier
the possibility of shuffling the stack is an attractive nuisance, like a tar pit. an occasional dup or swap or r@ is okay, but start by not using them
literally write square as
variable x
: square x ! x @ x @ * ;
or value x
: square to x x x * ;
if that's what it takes to stop tying your code in knotsit is ok to name a variable x, its scope only extends to the next definition of an x. it's not like in c where every x needs to either be local to a subroutine or a single global unique x used by the entire program (or at least source file)
later you can graduate to sometimes replacing one local variable with the return stack and another one or occasionally two with the operand stack, but it is better for your code to be kind of boring and verbose than an incomprehensible soup of 2dup -rot tuck
forth is maybe not better than c but there is no reason to make it worse
i just edited in a note about one of them, the scoping
for large programs forth has namespaces (vocabularies or wordlists) similar to static file scope in c
an important similarity between standard forth variables and c static variables is that they don't support recursion. in the occasional case where you do need recursion you can use the operand stack to save and restore your variables manually
x @ y @ ... recurse ...
y ! x !
which would not be possible in c with universal use of static global variablesalso though typically when you are stepping through forth you are doing it by typing names of words, while in c you use the debugger, which can see and change local variables
this allows you to make your words more like 'named lines of code' or 'named parameters' than like c functions, which is helpful for interactively poking at the system, and that's where forth really shines
like historically, as i understand it, the ui to the forth, inc. fullscreen text editor was a forth repl, just using a vocabulary with a lot of one-letter words for editor commands. that's not practical even with lisp; imagine trying to use emacs with only m-: as your keyboard interface. but with forth the interaction friction is only roughly as bad as with ed or ex, with the bonus that you can see what you're doing
in theory making your words smaller helps with composability as well, but having hidden state shared between the words in variables does tend to undermine that benefit imho
(Related anecdote: my summer job there back then was to review the new version of their system to find bugs, etc. There was this one piece which was ugly and awkwardly coded, and I was kinda happy to find it: finally, an excuse to rewrite something! It was the conventional interactive editor. Turned out it had been contributed by a customer; absolutely nobody at the company used it. Forth editor all the way.)
: square dup * ;
is better codebut the possibility of such clean, concise definitions is a seductive trap; it is easy to write code that isn't obviously correct in pursuit of that aim
quickly this leads you to code that is not just unreadable but full of bugs, and that is how the person who started this thread was, in their words, trying forth and failing; this is not just a common experience but nowadays a nearly universal one
so it is much better to err on the side of too many variables and too little stack manipulation than the opposite
you can always streamline the code later once it works
i'm pretty sure leo brodie would agree with me on this
my defensive tone was uncalled for
thank you for making the additional effort to clarify despite it
video games and motor controllers rather than computer algebra systems
the difficulties are the same as in c: you have to do your reallocation and deallocation and probably hashing manually
a surprising number of such problems turn out to work well enough if 'dynamically sized' becomes '1 billion or less' which simplifies the memory management dramatically
and you can write theorem provers and optimizing compilers and stuff in forth; it just isn't its strong point
1) losing track of the parameters in the stack (unlike Lisp, there are no parens to match and serve as containers)
2) everything is a number/pointer. STOIC provides strings, hash tables and more, along with parameter type checking.
Those are, in my opinion, the two main factors that keep Forth from being the "dual" to Lisp.
2over should be safe, legal, and rare
bigger words impede using the forth repl as a line-by-line stepping debugger, so they diminish the interactivity that is the reason you'd use want to forth in the first place
agreed about strings; one of the first things i wrote in forth was a straight port of bernstein's stralloc, last millennium
it hides the earlier declarations of the same name
If you're losing parameters in the stack, you need to refactor. The whole idea of wrapping things in brackets is what makes Lisp such an unreadable mess.
> 2) everything is a number/pointer.
If you don't understand why everything is a number or pointer, you don't understand anything about programming languages.
What would be a better interface for dealing directly with something like IO ports?
I have been tinkering with PostScript but not Forth, I grok that PostScript is a lot closer to LISP. A lot of people (without actually learning these languages/systems) say Forth and PS are similar, but I know that's not really the case - their resemblance is fairly superficial.
or maybe because you can't >r r@ r@ rdrop, or because garbage-collected [arrays] remove the temptation to write subroutines with 6 parameters
ps is fairly big compared to forth, not the 16 megs of libgs.so.9.50 but definitely a lot more than 8k
what they have in common is interactive repl orientation, user-defined control structures, great flexibility, lack of static types, and of course rpn
i was going to say 'closures' but of course ps's scoping is dynamic so it lacks them
Would not have done it if the C compiler for that target had been cheaper. We have better tools now. Back then, it was one of the few ways to do interactive development on really tiny machines. In that niche, it was useful.
it's unlikely to displace c or rust or python or unix or freertos or openocd
but it does have something special, like a delorean or something; unlikely to be forgotten either
one art
no more
no less
to do
all things
with art-
-lessness
(piet hein, grooks)