Live data from Hacker News

Thoughts on Forth Programming

call-with-current-continuation.org

1–10 of 97 posts

Re: Thoughts on Forth Programming

#2
> In Forth there is only memory - word and byte-sized cells of storage in memory, and stacks. You step down on the level of assembly language which may sound daunting, yet gives you full control over every aspect of memory layout.

Other than in niche or pet projects, can this even work?

If you deal with any sort of multibyte data like Unicode, doesn’t this become way harder?

Or do you just punt and use ascii code pages?

Re: Thoughts on Forth Programming

#3
post #2

> In Forth there is only memory - word and byte-sized cells of storage in memory, and stacks. You step down on the level of assembly language which may sound daunting, yet gives you full control over every aspect of memory layout. Other than in niche or pet projects, can this even work? If you deal with any sort of multibyte data like Unicode, doesn’t this become way harder? Or do you just punt and use ascii code pag…

You define words which are like functions which you could use to operate on multibyte data. Most forth code uses large dictionaries of words. So it will look like 'dosomething decode bytes' where bytes is your utf8 and decode is some utf8 decoder and so on.

edit: see https://rosettacode.org/wiki/UTF-8_encode_and_decode#Forth for a forth word for decoding utf-8

Re: Thoughts on Forth Programming

#4
This quote stood out as sounding quite far-fetched: "on many CPUs the interpreter consists of two or three machine instructions". Can someone point to an example?

Re: Thoughts on Forth Programming

#5
post #2

> In Forth there is only memory - word and byte-sized cells of storage in memory, and stacks. You step down on the level of assembly language which may sound daunting, yet gives you full control over every aspect of memory layout. Other than in niche or pet projects, can this even work? If you deal with any sort of multibyte data like Unicode, doesn’t this become way harder? Or do you just punt and use ascii code pag…

> Or do you just punt and use ascii code pages?

You're assuming that there's even ASCII. colorForth doesn't use it. It's all memory, but what happens is that you write a set of words that operate on the memory to form the abstraction you need, such as Unicode.

Re: Thoughts on Forth Programming

#6
post #4

This quote stood out as sounding quite far-fetched: "on many CPUs the interpreter consists of two or three machine instructions". Can someone point to an example?

See "Moving Forth": https://www.bradrodriguez.com/papers/moving1.htm

The "interpreter" is just the macro that handles execution of the next subroutine in a threaded program. As in: https://en.wikipedia.org/wiki/Threaded_code

It might only be a call instruction, or a pointer increment and a jump. If you have to move the pointer to the working register to increment it, that's three instructions.

Re: Thoughts on Forth Programming

#7
post #4

This quote stood out as sounding quite far-fetched: "on many CPUs the interpreter consists of two or three machine instructions". Can someone point to an example?

The book “Threaded interpretive languages” by R. G. Loeliger has details on how Forth & Forth like languages are built re: core interpreter & threading of words.

Re: Thoughts on Forth Programming

#8
post #4

This quote stood out as sounding quite far-fetched: "on many CPUs the interpreter consists of two or three machine instructions". Can someone point to an example?

In FORTH there is a distinction between the compiler and the interpreter. Each function in FORTH is called a "word". You define a word by giving it a name (historically only the first X characters counted -- often 8). The compiled words are stored in a "dictionary" which is essentially a key value pair with the name of a compiled word and a pointer to its compiled code. When you are compiling a word, you add a new entry to the dictionary. Then for each word contained in the new word you are defining, you look it up in the dictionary and store the pointer to its compiled code. So essentially, the compiled code consists of a list of pointers -- one for each function call you are making.

It's a little more complicated than that, but not much. Numerical literals need to be added to the stack rather than being treated as a pointer to a function, but there are a variety of ways you can tag the information you are putting in the list.

The interpreter works by taking a pointer to the function, and running it. This will essentially jump you to another pointer to a function which you will run. That will jump you to another pointer to a function which you will run. Eventually you will end up pointing to a function that was hand implemented in assembly language (part of the kernel for the language).

This technique is known as a "threaded interpreted language" (TIL). I haven't really explained it very well, but essentially it's just either pushing a literal onto the stack or jumping to a subroutine. The actual code for the interpreter is insanely small because it does virtually nothing (either jump to a subroutine or push a value onto the stack). While the entire thing is not 2 or 3 machine instructions, the actual running functionality would just be looping through something of that size.

One of the nice things about this setup is that it's pretty easy to write an entire FORTH kernel in 16 or 32K. So all of the actually executing code will fit in the cache of even a small processor. The rest of the code is literally lists of addresses and integer literals. They are super easy to fetch and you can also be tricky about optimising how you fetch them. The end result is that you barely ever hit main memory when talking about the code part of the system. And since you prefer working on the stack to working on the heap, you get really good locality on the working memory as well. This can give you insanely good performance with very little cognitive overhead as a programmer.

Re: Thoughts on Forth Programming

#9
post #2

> In Forth there is only memory - word and byte-sized cells of storage in memory, and stacks. You step down on the level of assembly language which may sound daunting, yet gives you full control over every aspect of memory layout. Other than in niche or pet projects, can this even work? If you deal with any sort of multibyte data like Unicode, doesn’t this become way harder? Or do you just punt and use ascii code pag…

This can work, but it's incredibly tedious and not worth the trouble. Writing in Forth is like writing in pure CIL or JVM bytecode (they're both stack machines like Forth). Sure, you can do it, but if it was so rewarding, people wouldn't be using Java or C#.

Re: Thoughts on Forth Programming

#10
post #2

> In Forth there is only memory - word and byte-sized cells of storage in memory, and stacks. You step down on the level of assembly language which may sound daunting, yet gives you full control over every aspect of memory layout. Other than in niche or pet projects, can this even work? If you deal with any sort of multibyte data like Unicode, doesn’t this become way harder? Or do you just punt and use ascii code pag…

This can work, but it's incredibly tedious and not worth the trouble. Writing in Forth is like writing in pure CIL or JVM bytecode (they're both stack machines like Forth). Sure, you can do it, but if it was so rewarding, people wouldn't be using Java or C#.

That's not true at all. I know multiple people who are incredibly productive in Forth environments, though I don't use it myself. Pretending that it's like writing in JVM bytecode (despite some overlap between the two) is completely unfounded.

Your claim that C# and Java won based on merits is similarly unfounded: they won because they have major corporate backing. There are countless languages before and after that did what they did, better. Limbo beats Java at almost everything Java aims to do, for example.

Post reply on HN