Live data from Hacker News

An attempt to articulate Forth's practical strengths and eternal usefulness

im-just-lee.ing

61–68 of 68 posts

Re: An attempt to articulate Forth's practical strengths and eternal usefulness

#61
post #47

Earlier quoted context omitted.

>I think it maybe undersells the importance of the REPL a bit. Howo? Or would you agree that value is perhaps a more suitable word than importance ? For me I think these articles have such a tendency to fixate on the strengths of Forth to the extent that they have reduced Forth to those strengths in the eyes of many. TFA does a fair job of avoiding this and shows Forth more as a powerful and flexible general purpose…

You can see my workflow with Forth in https://asciinema.org/a/621404 , which should help reinforce my point that I'm not an expert. What I mean is that Forth as a programming language is kind of... not great? Like, it's kind of hard to read and hard to write. For years I thought this might be just a question of familiarity, but not I'm resigned to the fact that I will probably not learn to read Forth as easily as I c…

I do have one objective thing to say about readability. In a pop infix language like C, Python, Lua, or JS, the expression

    e(d(), c(b, a()))
has fairly clear dataflow: data flows from a and b to c, and from c and d to e. This is knowable even without any previous knowledge of those five identifiers. The RPN version, in languages like Forth, PostScript, and Factor

    a b c d e
can just as well correspond to any of these dataflow patterns:

    a(); b(); c(); d(); e();  //none
    b(a); d(c); e();
    e(d(c, b()), a);
    {a, b, c, d, e} // all going somewhere else together
And many others. You don't know if a or b is consuming something left on the stack from before, either.

On this basis I think it's at least somewhat defensible to claim that stack languages are "less readable": information about the dataflow graph which is easily available in the infix syntax is not present, at least locally. You can reconstruct it by knowing, or guessing, the stack effect of each word. But that's different from just having it plainly written down.

As a result, in Forth and PostScript, I regularly have bugs where I pass a parameter to, or receive a result from, the wrong place. This is not a major practical problem (it's usually pretty easy to figure out in the REPL) but it serves as evidence that stack languages really do require more effort to read and understand than pop infix languages.

Of course, you can make almost exactly the same argument that explicit typing helps readability, and implicit variable capture by closures hurts it. I think there's some merit in that, actually.

Re: An attempt to articulate Forth's practical strengths and eternal usefulness

#62
I emailed this to Lee. I guess it can go here too.

---

I have been fortunate to have worked professionally with Forth recently. It was so fun! But I still struggle to point out exactly why I like Forth, and why and how it's different. Your essay is fresh take, which is good.

To me, maybe the most important lessons are.

1. Eschew complexity (sometimes to a fault), and 2. Improve the code by redefining the problem. Look at things from another angle. (I hate to say it, but think out of the box.)

Much of Forth falls out from these principles. E.g. people are quick to point out Forth is a stack based programming language. Which is true enough, but to me it's kind of beside the point. The point is the language does away with local variables (redefine the problem) to lay the ground for a much simpler implementation (eschew complexity).

Yes, there's REPL. But why? Because Forth is (or can be) a programming language, operating system, compiler, and command line rolled into one. Heaps of layers and components done away with.

File system, virtual system, code structure, documentation? Blocks!

The list goes on. Once you internalize this, the veil falls from your eyes, and you see how much needless complexity stands in your way in most other languages, operating system, tools, apps, ... it's everywhere.

Re: An attempt to articulate Forth's practical strengths and eternal usefulness

#63

>The project's README then proceeds into lengthy detail about how to implement >Forth in C. Seems people have/are more fun/interested implementing Forth interpreters/compilers than actually using Forth. Same with CHIP-8. It's all about making emulators.

Then you can have fun implementing Forth in Forth, with a metacompiler that compiles itself! Here's some of Mitch Bradley's beautiful code from OpenFirmware, his Forth kernel meta-compiler written in Forth, which supports 8, 16, 32, an 64 bit, big-endian and little-endian architectures, as well as direct, indirect, and token threaded code, with or without headers, etc: kernel.fth: https://github.com/MitchBradley/open…

In my mind, taking your toy Forth from implemented in C, assembler, or what have you, to metacompiled is transformative. I struggled at first, making a few abortive attempts. But when I finally did it, it was a revelation.

Re: An attempt to articulate Forth's practical strengths and eternal usefulness

#64
post #61
post #47

Earlier quoted context omitted.

You can see my workflow with Forth in https://asciinema.org/a/621404 , which should help reinforce my point that I'm not an expert. What I mean is that Forth as a programming language is kind of... not great? Like, it's kind of hard to read and hard to write. For years I thought this might be just a question of familiarity, but not I'm resigned to the fact that I will probably not learn to read Forth as easily as I c…

I do have one objective thing to say about readability. In a pop infix language like C, Python, Lua, or JS, the expression e(d(), c(b, a())) has fairly clear dataflow: data flows from a and b to c , and from c and d to e . This is knowable even without any previous knowledge of those five identifiers. The RPN version, in languages like Forth, PostScript, and Factor a b c d e can just as well correspond to any of thes…

Forth words are not functions and if you try and use them like functions, things will get messy. You should never have things like "a b c d e" in Forth, you should have "abcde," a single word with a descriptive name built from a, b, c, d, and e, and designed so all you have to worry about is the stack effect of "abcde" and not the words it is built on or the data flow. I would say this is like saying C is terrible because people do ridiculous things with macros.

I will give your posts a reread with fresh eyes tomorrow and probably have more to say, it is a bit too much to digest at this hour.

Re: An attempt to articulate Forth's practical strengths and eternal usefulness

#65
post #61

Earlier quoted context omitted.

I do have one objective thing to say about readability. In a pop infix language like C, Python, Lua, or JS, the expression e(d(), c(b, a())) has fairly clear dataflow: data flows from a and b to c , and from c and d to e . This is knowable even without any previous knowledge of those five identifiers. The RPN version, in languages like Forth, PostScript, and Factor a b c d e can just as well correspond to any of thes…

Forth words are not functions and if you try and use them like functions, things will get messy. You should never have things like "a b c d e" in Forth, you should have "abcde," a single word with a descriptive name built from a, b, c, d, and e, and designed so all you have to worry about is the stack effect of "abcde" and not the words it is built on or the data flow. I would say this is like saying C is terrible be…

Forth "words" are subroutines. C "functions" are also subroutines. Forth "words" are "not functions" merely because the use of "function" for "subroutine" is terminology specific to certain families of languages (C, Lisp) to which Forth does not belong. This is all irrelevant because I didn't say anything about "functions", just dataflow. My example expression is just as valid in Perl (which calls subroutines "subroutines") or Ruby (which calls them "methods").

A colon definition containing five sequential calls to different words without any control flow or stack manipulation is perfectly unremarkable. Here's a sample word from the F83 block editor, which I am using because F83 is generally accepted as highly competent Forth code, if not exemplary:

    n NEW moves the terminal's cursor to the start of line n,
      and overwrites lines until a line is begun with null input
      ( a Carraige Return).

    : NEW   (S n -- )                                                
       L/SCR SWAP                                                    
       DO   [ FORTH ] I [ EDITOR ] T  EDIT-AT >IN OFF QUERY SPAN @   
         IF  P  ELSE  [ FORTH ] I REDISPLAY  LEAVE  THEN  .SCREEN
       LOOP  .SCREEN  ;                                          
The phrase t edit-at >in off query span is just such a sequence, six words long rather than 5. The word off here is a standard word that sets a memory location to 0, and >in is the standard input-pointer variable, although you can't really be sure of that without more context—note, for example, that the editor vocabulary has redefined i as an insertion command rather than the usual loop-counter definition, thus the vocabulary switching commands.

The dataflow in that six-word code sequence is, in C syntax,

    t(i);
    edit_at();
    off(&in);
    query();
    &span ...
But to figure that out, I had to look up the stack comments of t, edit-at, query, and span (which is just a variable), and know the stack effects of >in and off. (In the Forth-83 standard https://www.complang.tuwien.ac.at/forth/fth83std/FORTH83.TXT query was the usual way to do what we do nowadays with accept—read a line of input, implicitly into tib.)

With slightly different definitions, this could easily have been, for example,

    edit_at(t(i));
    off(&in);
    span(query()) ...
Reconstructing the dataflow thus is not some kind of insuperable difficulty. It took me a while in this example, but were I more expert with Forth, and in particular Forth-83, I probably could have figured it out relatively quickly. If it were taking too much time, or if the stack comments were wrong, I could have figured it out interactively at the REPL, or single-stepping through the code in F83's debugger.

My point is just that reconstructing the dataflow is a problem you have to solve when you are reading Forth code. The author knew what the dataflow was, presuming it's working code you're looking at. An efficient compiler would have to know, too. But, as the reader, you don't know until you reconstruct it mentally with your global knowledge of the things it's calling. It's an extra error-prone decoding step between the code you're looking at and the semantic understanding you seek.

(I said "without stack manipulation", but stack manipulation generally makes the problem more mentally challenging, not less.)

By contrast, with pop infix syntax, the dataflow is represented locally with the parentheses and commas.

The same thing is true of types in languages with implicit typing (like OCaml and JS), of when call/return pairs follow a conventional stack discipline in Scheme (where the possibility of call/cc exists), of variable capture and escape in languages with closures, of which variables are mutated in languages like Python where all variables are mutable (and to a slightly smaller extent in languages like C, where immutability is possible but not default). The author has this information; the maintainer needs it to successfully modify the program; the compiler would need it to avoid producing grievously inefficient code; and there's no way to express it in the language.

My claim is that, while the optimum may be subjective on this spectrum between implicitness and explicitness, the implicitness itself is objective. You claim to disagree, but to me it sounds like you disagreed simply because you didn't understand what I was trying to express.

Re: An attempt to articulate Forth's practical strengths and eternal usefulness

#66
post #65

Earlier quoted context omitted.

Forth words are not functions and if you try and use them like functions, things will get messy. You should never have things like "a b c d e" in Forth, you should have "abcde," a single word with a descriptive name built from a, b, c, d, and e, and designed so all you have to worry about is the stack effect of "abcde" and not the words it is built on or the data flow. I would say this is like saying C is terrible be…

Forth "words" are subroutines. C "functions" are also subroutines. Forth "words" are "not functions" merely because the use of "function" for "subroutine" is terminology specific to certain families of languages (C, Lisp) to which Forth does not belong. This is all irrelevant because I didn't say anything about "functions", just dataflow. My example expression is just as valid in Perl (which calls subroutines "subrou…

Within the context of the four languages you listed and your example, I don't think Forth word vs function is merely terminology, it tells us a great deal of useful and important information. You clearly were able to grasp that but you worked under the assumption that I did not understand. There is nothing in Forth that requires you to make ambiguous constructs like that, you will come across them but like most every language (if not all), we sometimes trade readability for some other gain, or out of habit, ignorance, laziness, etc.

Re: An attempt to articulate Forth's practical strengths and eternal usefulness

#67
Well written in general. However:

> C++ has method override but it's not the same: you cannot change the behavior of how addition works on two 64-bit integers (such as treating them both as fixed-point numbers).

Wouldn't you just create a 1-field struct/class and override all the arithmetic operators? Or if you're less fixated about using the same operator (like me as a Lisper), invent a method called ADD and use that.

> Changing addition to work on "bignum"s (numbers that have arbitrarily large precision) is a good usecase of overriding the addition operation.

I don't see this as something unique to Forth compared to other languages, even C++.

Re: An attempt to articulate Forth's practical strengths and eternal usefulness

#68

There is an aspect of the history of Forth and C I have been trying to wrap my head around. The early B compiler was reported to generate threaded code (like Forth). The threaded code was abandoned fairly early in the port to the PDP11 from the PDP7 as it was deemed to slow to write an operating system in. At which point unix and C lost a very interesting size optimization. With the net result that Forth was more por…

I can probably shed some light on that. I've used Forth on 8 bit platforms (6502, 6809), 16 bit platforms (80286) and 32 bit platforms (68K), as well as assembly, and on the 16 and 32 bit platforms C. Putting these side-by-side and assuming roughly equivalent programmer competence levels at the time assembler would win out, C would get you to maybe half the speed of assembly on a good day and Forth was about 10x slow…

Thanks. That helps put things into perspective.

I looked and today Swift Forth from Forth Inc, has some version of a compiler that generates not horrible code so I expect the 10x slowdown compared to C is a thing of the past.

Post reply on HN