Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I'll agree with a lot of his critiques, but one of his small ones that surprisingly echoed a lot of frustration was the dictum to prefer small definitions.

> Quoting Chuck Moore:

> Forth is highly factored code. I don't know anything else to say except that Forth is definitions. If you have a lot of small definitions you are writing Forth. In order to write a lot of small definitions you have to have a stack.

It seemed like apologetics and a making a virtue out of necessity, given the fact that I don't have the capability to do stack acrobatics in my head live. The only way to be able to read a function in my head, without taking a pencil to paper _was_ small functions. But I found that clashed with the ways some algorithms and procedures naturally expressed themselves in longer multi-step style, and actually ending up being more verbose and tangled with multiple top-level definitions.

It turns out that local variables that compile to C-style indirect (SP + i) accesses are only mildly more expensive than stack acrobatics, but still gave the flexibility of Forth-style metaprogramming. [1]

Ultimately, the author's points about the "Forth philosophy" but not Forth-the-language itself (and extremely spare code) ring true to me.

Given my limitations, life is too short to work to have as minimalist an implementation as you'd like, and to desire to have a interactive development environment in <128k. For me, it's hard enough to implement the "subject" that I'm programming algorithmically/data-driven-ly/amortizing-computation-ly efficiently.

[1] https://www.novabbs.com/devel/article-flat.php?id=26347&grou...



It's easier to approach Forth as assembly-like than C-like.

That is, why are you factoring the code to use the stack when you have globals?

(Mumble mumble structured program, recursion, reuse)

If you move towards a global-first approach(which is what Chuck Moore seems to have moved towards, from anecdote), what changes is that you can substitute the word defining the variable for another word, later down the line, that adds the context, indirection and error handling you need. The mechanism can be added without changing how the word is being used, and you can still write a divide-and-conquer kind of algorithm in this way, it's just more classically imperative in style, with more setting of mutable global temporaries and every byte of bookkeeping directly accounted for.

Part of the minimalist freedom in Forth is that it is agnostic to whether you're using the stack or the dictionary. If you want the word to be unambiguously about a particular space in memory it makes sense to define it first in terms of "it accesses this static location" instead of "it consumes three things on the stack and shuffles them around and indirects one to a memory location and adds the other two", because that inclines all the words to be about the stack. Take the primitive approach - the one that maps well to assembly - first and see how far it goes. You stay in control of how you're extending the language that way. C preempts that because the compiler hides the stack manipulation, so the semantics of the function will default towards locals, and then further extension is guided around fitting it into that.

(And it's true that the compiler gets you to an answer faster, and black-boxes the interface, so you can use code without reading code - and that is coming at the expense of precision around details like this. Forth is probably not the right way, if it's Conway's law that you're up against.)


In most assembly languages, accessing local variables on the stack is easy, plus you have multiple registers for temporary data. Forth feels extremely limiting compared to that.

On an architecture without those features, like the 6502, Forth may be a good idea, and possibly faster than C - but only if it's compiled to machine code with some peephole optimizations, so that e.g. "123 MY-VAR C!" translates into "LDA #123 ; STA MY-VAR", instead of a naive implementation where the address and constant would first be pushed onto the stack.

And any more complicated optimizations would probably require first "decompiling" the Forth code back into a higher level of abstraction. It's practically the same as assembler macros otherwise.

edit: fixed order of operands. I originally wrote "MY-VAR 123 C!", but then remembered that the address to store to has to be on top of stack. IMHO, infix notation is less confusing, and writing a recursive-descent parser to handle it isn't that hard compared to everything else in implementing a compiler. And of course in an infix language, "123 := MYVAR" would be a syntax error, instead of storing (the low byte of) the address of MYVAR into memory location 123.


I appreciate your nudge towards thinking in a global-style manner – it does remind me of the style that early Macs and PCs wrote their software in, with memory and all lifetime data accounted for before the algorithms. (Maybe instead of the structured programming "Algorithms and Data Structures" (Wirth) approach, it's "Data Structures and Algorithms".)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: