7 ms·
Understanding What It's Like to Program in Forth (2008)
- stewbrew 7y ago"Better than Sudoku? Yes." I would have went with riding a 5 EUR motorized kids skateboard without any protection and never looking back. But that's a good analogy too.
- LessDmesg 7y agoThe thing that's wrong with Forth is it promotes eschewing verbal identification of variables for the sake of positional. This is promoted as good mental exercise, but is just bad language design. As Rich Hickey said, we humans prefer to identify things in terms of keywords, not positions. For any method with more than a couple parameters, kwargs are more convenient than positional arguments or, worse, implicit args on the stack. This is why concatenative languages are doomed to be just a forgotten cul-de-sac off the highway of the history of programming languages.
- andybak 7y agoMaybe the only problem here is that Forth only really properly supports integers (and floats on a separate stack). If your language didn't come bundled with Vector support you can bet that adding two vectors wouldn't be terribly pretty either. Or at least it wouldn't merely be "+" One can imagine "Forth with a typed Stack" where this would be as simple as "+". Also - Forth can do named identifiers. It's just not the goto technique.
- lebuffon 7y agoThat's why OForth was invented. http://www.oforth.com/ http://www.oforth.com/
- ken 7y agoI frequently see arguments of the form "X is good; Y doesn't have X; therefore all typeof(Y) are doomed". I don't understand. Didn't you just give a criterion for success? Why can't we make a new Y2 that has X? In this case, it doesn't even seem difficult. I've seen multiple stack-based systems that allow items labels. They don't use them for arg-passing (AFAIR), but I don't see why they couldn't. I'm not even convinced that positional arguments are fatal for language design. Most popular languages today lack named arguments. They're usually only named on the callee side. I'm sure we've all run across a C function call that looked like foo(a, b, c, d, e, f, g, h), and cursed the author under our breath, and then counted out to the argument number we wanted to change. Languages succeed or fail for many reasons, but I don't think I've ever seen "calling convention" be the deciding factor (no pun intended). In the more general sense of concatenative (not necessarily stack-based) languages, Unix pipelines are a common example, and they don't seem to be doomed.
- deleted 7y ago[deleted]
- carapace 7y ago> concatenative languages are doomed to be just a forgotten cul-de-sac off the highway of the history of programming languages. I gotta disagree. I've been working with Joy the last couple of years and I think it's possibly a "silver bullet". Interestingly, it sidesteps a lot of the problems with stack manipulation by means of flexible combinators that permit various kinds of higher-order stack/expression manipulation. - - - - Also, see "Compiling to Categories" wherein Haskell is converted to a kind of point-free form very similar to Joy... https://conal.net/papers/compiling-to-categories/ https://conal.net/papers/compiling-to-categories/
- dwheeler 7y agoMany Forth developers do not like using named parameters AKA local variables. That said, they certainly exist, so if that is your only objection to Forth then there are easy ways to solve it. See, for example, http://zedcode.blogspot.com/2011/02/forth-parameter-stack-named-parameters.html http://zedcode.blogspot.com/2011/02/forth-parameter-stack-na...
- dwheeler 7y agoI've since discovered that the latest Forth standard, Forth-2012, includes an optional set of Forth words to support local variables. Gory details here (warning, spec-ese): http://forth-standard.org/standard/locals http://forth-standard.org/standard/locals I don't know how widely these are supported in Forth implementations.
- kragen 7y agoAll variables in Forth are local variables. They're just, traditionally, statically allocated, like a static variable in a C program. Their scope extends from their declaration to wherever the wordlist they're defined in stops being visible, or until another variable of the same name is defined, which means their scope usually does not extend to the end of your program. I think the spec's choice to term stack-allocated variables "locals" (for what we call "automatic" variables in C) is a bad choice, because it confuses the issue further. When we refer to "local" or "global" variables, normally we are referring to scope, which is a question of over what part of a program a name maps to a particular location (or, in functional languages, sometimes a value). But what we're talking about here is extent or lifetime: during what period of time is a particular location allocated. These are clearly not unrelated concerns, since a name cannot usefully map to an unallocated location, and the same static occurrence of a name can map to multiple different locations when that name is bound on different binding contours, which cannot happen when the name is bound in the global scope; but these two concerns are also not identical, and I think it is important to distinguish them. In Scheme, for example, the scoping rules are the same block-scoping rules we're familiar with from Pascal and C; but the extent of all Scheme values is unlimited — they can continue to exist until the end of the program, regardless of which subroutines have returned. This is also true in the ML family (OCaml, Haskell) and the abundant crop of modern Scheme-descended languages, including JS, Perl5, Python, Lua, and Ruby, but not in C++ or Rust. (Most, though, don't follow Scheme's mind-bending lead in having first-class continuations with unlimited extent, though Ruby does.) So, what the Forth-2012 locals word set provides is names with dynamic or automatic extent: they refer to values that exist during a particular activation of a particular function. It also provides local scoping for names for them, but this is a much less significant change.
- lebuffon 7y agoIt's an assembly language for a 2-stack VM. It's never going to be easy but it is hella-fun to play with.
- mark_l_watson 7y agoI experimented with Forth with my Apple II (I had serial number 71, one of the early ones!). I liked the interactive bottom up REPL coding style, but I found it difficult to read code that I had written the week before. After playing with Forth, I bought Pegasus Lisp for the Apple II and for me Lisp hits a sweet spot of concision, bottom up REPL development, and readability. That said, I can see why some people like Forth if they are very experienced with it and can get things done quickly.
- timbit42 7y agoReading the book 'Thinking Forth' will help you write legible code.
- carapace 7y agoI second this, IMO to a first approximation reading that book is the way to understand programming in Forth. http://thinking-forth.sourceforge.net/ http://thinking-forth.sourceforge.net/
- deleted 7y ago[deleted]
- kazinator 7y agoI played with something called GraFORTH on my II+ clone.
- imglorp 7y agoI was following along until the author stopped explaining new syntax. "over", according to forth.com, copies second item to top... ( n1 n2 — n1 n2 n1 ) But I get lost with >r, r>, r@, and !
- loa_in_ 7y agor@, r>, >r are operating on return stack, another stack that's dedicated to return addreses and control structures (intermediate states of for loop for example)
- loa_in_ 7y agoThey move top cell from stack to return stack or vice versa. r@ and r! are effectively equivalent to r> @ and r> ! ... or was it r> @ >r and r> ! >r
- co_dh 7y agoThat is an unfair comparison. Forth is designed to run on metal, with no os, no stdlibc, no vector. Can any of other language do that? In fact, by just adding first class function, retro forth makes the code way easier to read.
- davidgay 7y ago> That is an unfair comparison. Forth is designed to run on metal, with no os, no stdlibc, no vector. Can any of other language do that? Not much of a stretch with C, really - there's a reason its used to write operating systems ;) (the standard library is mostly about OS interaction which is irrelevant in the bare metal/no OS context) On some systems, C requires a minimal library for compiler-emitted intrinsics (e.g., for 16/32-bit multiplies on 8-bit systems, for floating point, etc).
- madhadron 7y agoI think you misunderstand. When the parent says "designed to run on metal" they don't mean "designed to be cross compiled to produce binaries that run on metal", they mean "the compiler, editor, etc. are all running on metal."
- mikekchar 7y agoOne of the very first things I thought of when I heard about WASM is, "Oh.. It would be so fun to build FORTH on that". I never got around to it (yet) and from a quick search, there are already a ton of implementations. But I think this is one of the things about FORTH: it's not just that you can run it on the metal. It's that you can easily build a FORTH environment for pretty much any metal you can think of. Bootstrapping from absolutely nothing to a very productive environment is really an empowering feeling. I always thought it was a shame that there are so many people who have made a Lisp, but so few that have made a FORTH. I kind of feel like both of these tasks should be required material in school... Though I only have nebulous feelings about why it is valuable... Not sure I could explain it to someone who hasn't done it already.
- dwheeler 7y agoForth is truly amazing in terms of what you can do with very little RAM and CPU power. It is quite possible to have a working interactive development environment and production implementation in only 4-8 kibibytes of RAM. In the world of small memory 8 bit computers it was amazing. I think Forth can be useful to learn today, at least to stretch the mind to realize that not all programming languages have to be variants of Algol. It can also be a fun game as suggested by the article. I have enjoyed fiddling with it, including re-implementation of the language, and I can recommend it to others as a fun Bazaar toy. In addition, the book Starting Forth is amazing and still worth reading, because it shows that complex topics can be explained clearly and with humor. However, for actual production use, I do not think the Forth programming language is ever going to return to widespread actual use. Our computer systems are far more capable, and if you have to program a tiny of computer, you would use a larger computer for programming instead of trying to fit your development environment within a few K of RAM. Nowadays the big constraint is development time, including the time to develop the resort code the time to read existing code to modify it. Forth doesn't do as well by those measures. When something is trivial another languages, but it's a puzzle to solve in Forth, that is not a good thing. That does not mean that Forth is a bad programming language, it is a very good programming language, but it is designed and optimized for a situation that is increasingly unlikely.
- ddingus 7y agoI have known an embedded engineer for probably a decade now. He uses Forth. I watched him write a tiny forth kernel in a Kb of RAM or so, then run a dictionary through it to get a brand new CPU bootstrapped in what could be as little as a day. Very impressive. That system got an assembler / disassembler used to make new system words and other words fast. Modifying a Forth system, and I say that due to how Forth is really an environment that becomes capable of the application, depends lot on who made it and what one can know of their thought process. What I thought interesting about this guys approach us a fairly pure Forth is used to bootstrap. Then one that is a lot more comfy gets made for the longer get term. Once that stage is reached, and there are good tools, assembler / disassembler, monitor, word viewer, etc... Forth can be made less of a puzzle. One can even add other syntax, an interpreter, if desired. The product is more like a dev system, self hosted and capable. I find it all compelling, but as you say, much different.
- kragen 7y agoJames Hague has a lot more Forth experience than I do. But I would initially write this function something like this: variable v1 variable v2 variable v3 : vadd v3 ! v2 ! v1 ! 3 0 do v1 @ i cells + @ v2 @ i cells + @ + v3 @ i cells + ! loop ; Or, alternatively: 0 value v1 0 value v2 0 value v3 : vvadd to v3 to v2 to v1 3 0 do v1 i cells + @ v2 i cells + @ + v3 i cells + ! loop ; I mean, yes, you can make it a puzzle. But you don't have to. These versions will be slower than Hague's first working version and probably the second too, but all of Hague's versions are slower (with any Forth compiler I've used) than what GCC will give you for the C version. And what GCC will give you for the C version, even if it improbably manages to vectorize, is also a lot slower than what you'll get if you do your vec3 math on your GPU. So maybe when it comes time to rewrite the naïve Forth version above, because it is slow, you should just code an SSE assembly routine and skip the manual loop unrolling at the Forth level, or restructure the whole system to do the vector math on the GPU. Or maybe not. James Hague is a smart guy and he's shipped a lot of code, in assembly, C, and Forth. But I'd like to understand where my reasoning is going wrong, because I can't see it. My perspective on Forth is that it's the thinnest possible layer on top of raw machine code that gives you all the fundamental mechanisms for improving your leverage. You need structured control flow, so it has IF ELSE THEN, DO LOOP, BEGIN WHILE REPEAT, and a couple of others. You need expressions, so it has an operand stack. You need procedural abstraction, so it has a return stack, which also serves to store loop counters. You need labels both to make your code readable and so that you can patch your code without overwriting parts of it with JMP instructions to wherever you have free space to put the patch code, so it has words. You need metaprogramming, so it has IMMEDIATE. You need run-time polymorphic procedure dispatch, so it has ', EXECUTE, DEFER, IS, and CREATE DOES>. You need namespace management, so new definitions hide old definitions with the same name, and you also have wordlists. The problem with all these wonderful mechanisms is that they can become an attractive nuisance, as you futz around trying to find the perfect way to express your intent in the smallest, clearest amount of code, navigating the tradeoffs imposed by the sparse toolset. But you don't have to do it that way. You can just write simple, straightforward code that gets the job done with no fuss, and move on to the next problem. Forth lets you do that, too. That said, I find it easier to get my code working in assembly language than in Forth, and I don't know if that's because I don't know Forth well enough, because Forth is inherently less practical than assembly language, or just because Forth somehow fails to fit my brain. My guess is that I fall for the attractive nuisances too often and waste my time with unproductive futzing. And that's what this blog post looks like to me.
- samatman 7y agoRan across this post years before I started playing with Forth (recommend the whole blog, he's a great writer). Later I was going through a Forth phase and came back to it to tackle the puzzle it poses, here's my take on it: https://mnemnion.github.io/blog/2014/01/12/those-dadgum-adders/ https://mnemnion.github.io/blog/2014/01/12/those-dadgum-adde... I hope this illustrates the raw power of the Forth paradigm a bit better than the OP.
- robomartin 7y agoSometimes I feel discussions on HN always go into the weeds with people going back and forth while losing sight of reality. I used Forth professionally for well over a decade. I made truckloads of money with the end products. One of the companies I was involved with, which was 100% Forth, sold for just south of $20 million USD. Customers don’t go to Home Depot to buy drill bits. They go there to buy holes. The world could not care less if solution A was coded in C, solution B in Forth and solution C using a keypad to enter machine code. All they care about is the best solution at the best price. I have always felt that the distinction between experienced and less experienced engineers is that those with experience focus on what matters —delivering value to customers— while the less experienced folk needlessly burn time on theory, languages, code editors and other stuff. 25 years ago I made over $500K with a small 6502 embedded system used to help control lab equipment during cancer research lab tests. I made this box with a 6502, a couple of UARTS, a 2x16 LCD display and four pushbuttons. The box sold for $2,000 each. Sold piles of them. The code was written in Forth. The buyers could not care less. Neither did I frankly. I love Forth, but, at the end of the day, it’s just another tool. Customers want holes.
- ggm 7y agoCustomers want holes. At one level I entirely agree. But at another, when it comes to the holes they drill in the bathroom to hold the toilet roll, and the holes they believe were drilled in the pickle fork holding the wing onto the 737-max, they feel very differently about the holes. The first one, They don't much WANT The toilet paper falling off, but its kinda meh. The second one, being told uncle fester was given a hand drill from the barn and told to ream 'em good and deep doesn't fill anyone with joy. Customers want the right kind of holes.
- robomartin 7y agoThese kinds of analogies are not intended as precise engineering or business definitions. They are simple thinking tools used to deliver a point. If you are in the business of delivering holes for aerospace customers your focus should be on delivering the kinds of holes they need. At the end of the day, they still only care about holes. That’s what they are buying. Most customers using a software-based product could not care less if it was created using vim and python or toggle switches and machine language. They are after the solution to a problem; that’s all they care about. Our customers don’t buy language features, compiler flags, stacks, lists, dictionaries, classes, variables or functions.
- ggm 7y agoWhen we did forth(like) languages in computer science kindy (back in the 70s and 80s) the lecturers used to smirk because real languages like ADA took 20 passes and built huge models and were therefore more betterer and gooderer for some value of complex. Having the thing function as a stack machine in Reverse-Polish meant you didn't have to parse anything, it was basically already there for you sitting on the top of the stack (dock of the bay) watching the PC come in. I think the smirk was misplaced. What they needed to do was talk about this in comparative language classes and about why people drive to small(er) languages, and fitness for purpose, and what kind(s) of things happen when you force people to think this way, vs why you drive to COBOL and what you force people to think and do, when you go there. They're all turing-complete. They're all capable of expressing the same things, at some differing level of complexity and cost, in some variant of machinery.
- OneWingedShark 7y agoIf it were up to me to design curricula for CS, I'd do the entire thing in Ada, with the exceptions of "Data-Structures & Algorithms" being in Lisp and "Machine Architecture" in Forth — both of these classes would have a "Compilers & Interpreters" prerequisite where they would be provided the specification-files for Forth and Lisp interpreters/compilers which the students would implement, and that is what they would use in the aforementioned classes — not only would having the curriculum in these less-popular languages help emphasize that the important part is the underlying-theory (rather than hyped-up language-of-the-month), but making its prerequisites actually matter and introducing an element of software-maintenance would do a lot to produce good graduates.