Hacker News new | ask | show | jobs
by wrycoder 2589 days ago
Write-only, because only the functions (‘WORDS’) are named - no parameter names. There are also no local variables in standard Forth, you have to push (and pop!) them from the separate return stack. Accessing parameters is done by manipulating the stack, which makes the code opaque after a year or so. Named constants and variables are all global. So, it is very difficult for a team to use, since most of the legibility of software comes from insightfully selected names. Two thirds of those names, at least, are missing in Forth.

Like assembler, Forth is untyped.

These failings could be at least partly forgiven if Forth was fast. But it isn’t, because most words are short and all the stack manipulations are done with subroutine calls. It’s possible to write an inlining, optimizing compiler, but then the simple layout of code in memory is lost, and quick introspection using a memory dump becomes nearly impossible.

But, it is a neat tool to bootstrap a working system quickly on new, embedded hardware with limited resources. Definitely a black belt tool for lightweight systems written by a single person. Hard on maintenance programmers.

See Jonesforth for a tutorial example.

I wrote a Forth a few decades ago. The insight gained reminds me of Nand2tetris, but the latter is far more mainstream.

3 comments

all the stack manipulations are done with subroutine calls

This can have an advantage: one can stuff much more code (and functionality) in a very memory limited system (e.g. smart cards), especially with WORDS compression:

"Huffman threaded code is one of the most compact representations known for a computer program"

https://en.wikipedia.org/wiki/Threaded_code#Huffman_threadin...

> Write-only, because only the functions (‘WORDS’) are named - no parameter names.

Depends on who is the writer...

> There are also no local variables in standard Forth

Named locals variables are in the standard, as an optional extension. Because not everyone want them.

> you have to push (and pop!) them from the separate return stack

That's the main reason why the return stack is exposed to the programmer, yes, but there are other techniques like putting some of the data that are used throughout the program in (global) variables. you generally choose depending on how much simplification you gain and how much modularity you lose etc.

> Accessing parameters is done by manipulating the stack, which makes the code opaque after a year or so

Depends on how you write and comment.

> Named constants and variables are all global. So, it is very difficult for a team to use

Standard Forth as vocabularies to isolate functions, constants, and variables that is supposed to help with name clashes. Many variants like Retro have some sort of namespace mechanism.

Did you actually use Forth in a team context?

> These failings could be at least partly forgiven if Forth was fast. But it isn’t, because most words are short and all the stack manipulations are done with subroutine calls

Not a problem, because you design your programs to minimize stack juggling. Less overhead, more readable, one stone, two birds.

My dialect which uses very naive subroutine threading (it just executes lists of function pointers) can rival with Lua (non JIT) on certain tasks because my Forth doesn't spend its time doing useless stuff like looking up an hash table.

The speed of Forth is not in its bytecode interpreter. It's in the simplifications it encourages.

> It’s possible to write an inlining, optimizing compiler, but then the simple layout of code in memory is lost, and quick introspection using a memory dump becomes nearly impossible

I have written a few decades such a thing for the 8086, and I would say the lack of introspection was never a problem. When you write such a system, the hex machine codes sticks quickly in your head. I think I wrote a disassembler for the lulz, but an hex dump was probably my main "introspection" tool.

> I wrote a Forth a few decades ago

I wrote several Forth systems in the last few decades. The latest one I wrote is my goto-tool and serves me almost daily.

> Depends on how you write and comment.

Unless working in assembly or something of that sort, tend to I comment only on noteworthy matters pertaining to specific logic, not recurring units of basic program structure and their routine connections.

The only "extraneous" comments related to the point-free style of Forth are the "stack diagrams" showing the inputs and outputs of a definition/function, when it's not obvious from the code (it very often is when you write one-line definitions).

This is no different from what people do with Doxygen. Nobody prevents anyone from using a Doxygen-like tool with Forth. Or even literate programming if that's your thing.

Inside definitions, if you have to insert a comment to remind yourself what the state of the stack is at that point, very often it means you have failed in some way. Some refactoring or rethinking is needed.

> Nobody prevents anyone from using a Doxygen-like tool with Forth.

Nobody prevents anyone from writing a perfect solo project from scratch in which their idea of best practices is assiduously adhered to. Then there are projects where other people don't follow one's favorite practices, and projects one inherits that had previous maintainers which didn't follow those practices.

Yes, sure. That's why you have VBA, Java, Perl, C, Newlisp, Fortran, Python projects that are maintenance nightmare. Forth is nothing special with regard to this concern. Code quality has a lot more to do with the skills of the writer than the peculiarities of the language.

Except perhaps that you are more concerned with code quality when you know that it will be hard to read a couple of months later if you are sloppy.

Forth is unforgiving in many ways like an old master. There are people who call him a bad teacher, and there are people who listen to him. This is the difference between good students and bad students.

Anyway, the fact that Forth, Inc., has been in business for forty years and is still taking new commercial projects [1] is enough of a proof that Forth is more than just the code written by hobbyists you see on GitHub.

[1] https://wiki.forth-ev.de/doku.php/events:ef2018:forth-in-tha...

> Forth is nothing special with regard to this concern

When we look at a clump of Forth code that's supposed to be a function, unless there is commentary, we don't know how many arguments it takes or how many values it leaves on the stack.

We don't have this problem with VBA, Java, Perl, C, Newlisp, Fortran or Python.

Forth is an assembly language for a pretty low level stack-based virtual machine. (And such machines are very popular, though rarely programmed by hand.)

It's not a good VM for compiling dynamic languages; it provides no run-time check on mismatched function arguments, and generating code to do that would be inefficient and incompatible with regular Forth function calls. (E.g. we can have a calling convention whereby we push the argument count that the callee will pop and check.)

Forth could be a good target for a static language where function calls are checked at compile time against declarations.

> Forth is unforgiving in many ways like an old master.

Forth is completely forgiving, like an old buffoon. Pass four arguments to a three-argument function? All is forgiven.

> Write-only, because only the functions (‘WORDS’) are named - no parameter names

You could use lambda notation to name the parameters of a function, while still preserving postfix function application as in FORTH. This would effectively give you De Bruijn notation, in which named lambda was often denoted `[var] expr`. Though, ironically, De Bruijn is also known for his "nameless" De Bruijn indexes, which dispense with the very naming of function parameters, replacing these names with what are in effect references to positions on some abstract argument stack.