Hacker Newsnew | past | comments | ask | show | jobs | submit | nalsirety's commentslogin

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: