Great article. I recommend reading yosefk’s article with it. I’m going to draw on it as I counter some of this one.
“due to its roots in simplicity and compactness, considering that it emerged in the microcomputer era, on machines that provided (for todays standards) severely limited
capacity.”
“You step down on the level of assembly language which may sound daunting, yet gives you full control over every aspect of memory layout. There are no articial barriers between you and the machine’s architecture”
This is only partly true. It’s other root was in the personal preferences of Chuck Moore. That’s why it’s 18-bit, stack-oriented design in an era of 8/16/32/64-bit stack and register machines. An 18-bit stack on a 32-64-bit RISC machine is neither the simplest nor most efficient way to do things. Even if aiming for low gates, the billions a year in 8-16-bit MCU’s sold tell me there’s people getting by with less. The constructs themselves aren’t close to how we think about a lot of operations. Languages like LISP and Nim have metaprogramming that let us write constructs close to how we think that get turned into effecient primitives we still can understand. They have some benefits of Forth without the downside of putting square pegs into round holes.
So, Forth is the result of both squeezing what one can out of constrained hardware/software as author says and arbitrary decisions by Chuck Moore. It’s easier to understand some of it when you remember that one person just liked doing it that way, refused to adopt any method from others, and tried to force everything through his limited tools. He’s actually like a for-profit, industrial version of the demoscene. It’s cool seeing what he does. It’s also objectively worse than what others are doing along a lot of metrics. Especially hardware where his tools can’t handle modern, deep-sub-micron designs with their speed, size, cost, and energy benefits.
So, I default on thinking that future work in Forth should attempt to isolate and ditch arbitrary restrictions to build a similar model of simplicity for current CPU’s or hardware design. A modern CPU is 64-bits w/ registers and stacks, several levels of cache, SIMD, multicore, maybe hardware accelerators, supports memory isolation, and supports VM’s. What does the simplest implementation of that look like in software? For hardware, it has to be FSM’s that are synthesized and resynthesized in a series of NP-hard problems on manufacturing processes that have up to 2,500 design rules at 28nm. One problem even requires image recognition on circuits themselves before redrawing them. What does the simplest, EDA tool for that look like? Answering such questions gets us the benefits of Moore’s philosophy without the unnecessary baggage.
Hint: Microcode and PALcode. They’re like the ISA version of defining Forth functions. I want microcode I can customize with open tools in every CPU. :)
“Imagine being able to debug device drivers interactively”
This is an argument for interpreters, not Forth itself. Forth is one possibility. An interpreter for a subset of C or superset of assembly (eg Hyde’s HLA) w/ live debugging and code reloading is also possible. I’ve thought about building one many times. I’d be shocked if there weren’t already a dozen examples.
“You can’t have both a language that provides direct low-level access to the machine
and that at the same time give you all advantages of a high-level environment like automatic memory management.”
These languages exist. They’re safe, systems languages that allow inline assembly. There’s also typed and macro assembly languages. Recently, there’s work on type systems to know combinations of them behave as expected. You can’t do just any arbitrary thing. You can mix approaches where you want, though.
“It is satisfying and reassuring to be able to fully understand the code that is generated from your sources”
Wirth showed you can do this with a language built for human understanding. His languages are simple enough that students design compilers for them. One could do an interpreter even easier. It’s pretty clear what object code will come out of it. And a little discipline can prevent things like cache misses. Worst case, you just look at the assembly of each module to see if you want to tinker with it. As with any other goal, it’s possible that Forth takes simplicity too far. I thought Wirth did that, too. I think there’s tradeoffs to be made between simplicity and complexity. Forth philosophy, like Wirth’s, will sacrifice everything else to simplicity. I’m fine with a more complex implementation of a language if it lets me express myself faster, safer, and so on with efficient code coming out.
“But why do so many insanely complex protocols and architectures exist?”
Author is right that a lot of complexity is unnecessary. The author also writes like all complexity is unnecessary. This is incorrect: much of it is intrinsic to the software requirements as Brooks wrote. Even a simple design becomes more complex if you handle stuff like error handling, software updates, backups, security mitigations, and so on. Even Forth itself is more complex than a native alternative with similar primitives and macros since it will have an interpreter/compiler/updater built in. Forth’s people think that complexity is justified over bare metal since it gives them benefits in the article. Well, that’s what we’re saying about a lot of this other complexity in CPU’s, OS’s, and languages.
“It means to drop the bells and whistles, the nice user interfaces”
User adoption, onboarding, and support is apparently not a thing in the Forth world. Both market share and usability studies showed GUI’s were superior to text for common applications for most users. So, we should give them what they want, do what works, or however you want to look at it. If we don’t need it, then don’t implement it to reduce complexity. There’s plenty of terminal users or services that don’t need GUI’s. That’s not them being inherently bad like author argues, though.
“What you reuse of a library is usually just a tiny part, but you pay for all
the unused or pointless functionality and particularly for the bugs that
are not yours.”
Author massively overstates real problems that come with bringing in dependencies. Lots of libraries are simple to use and easy to swap out. The OpenBSD and Suckless camps regularly crank out simple implementations of software. Even the complex ones like Nginx often have guides that make installing and using them simple. The complexity is dealt with by somebody else. Likewise with these hosting providers that make it simple to get some services running at almost no cost.
I imagine many businesses and products wouldn’t happen at the rate and cost they do if each developer spent tons of time rebuilding compatible networking stacks, web servers, and browsers from the ground up with deep understanding of them. Nah, they just install a package, read its guide, and Get Shit Done. You could say we opponents of Do It Yourself just like to Get Shit Done in Whatever Way Works. If you do API’s and modularity right, you can always improve later on anything you had to keep shoddy to meet constraints.
Forth was the first programming language I encountered. I’ve rarely used it (debugged from the openboot prom once, wrote some minor things in Quartus on the palm pilot) since learning it as a child in 1978. There’s a joke that if you’ve seen one Forth, you’ve seen one Forth. This rings true because there’s nearly nothing to Forth but a couple of primitives everyone more or less agrees upon, a standard everyone ignores, a philosophy, and the problem at hand. The solution is to build up the language and it becomes a DSL for that specific problem, as understood by that particular programmer, and becomes weird.
I really enjoyed this article: the author has clearly put a lot of thought into it.
In my mind — and I consider myself one — I would use the term ‘modernist’ to describe this way of thinking. The idea that there is enlightenment to be achieved through endless refinement and attention — that coding is precisely an art, something worth this incredible degree of attention despite losing any semblance of productivity.
Now I really do think of coding as an art too, and I wholly encourage the author to enjoy this artistic aspect of coding they’ve found! But under no circumstances should this be considered the only artistic avenue when it comes to code. Sure, there’s an art to creating the most minimal, featureless, compact, piece of forth, but there’s also an art to creating the very best and most intuitive user interface, and an art to jumping into a hundred thousand line code base and getting your bearings, an art to creating languages that do try to span the divide between close-to-the-hardware and woderfully-abstracted, even an art to being the most ‘productive’, hammering out the most number of features in a short amount of time.
Absolutely enjoy your minimalism — maybe I’ll join you! — but there are many ways to have fun programming, and all should be acceptable.
After spending a few months with Forth earlier this year, I absolutely agree that Forth can be extraordinarily simple and compact, that mainstream software is an endless brittle tower of abstractions, and that the aha moment when you find the right abstractions of a problem can be transcendent. But writings like this also indicate limitations that the Forth community unquestioningly accepts.
Forth is quite “individualistic”, tailored to single persons or small groups of programmers.. nobody says that code can’t be shared, that one should not learn to understand other people’s code or that designs should be hoarded by lone rangers. But we should understand that in many cases it is a single programmer or very small team that does the main design and implementation work.
This is fine. However, the next step is not:
once it becomes infeasible for a single person to rewrite the core functionality from scratch, it is dead. The ideal is: you write it, you understand it, you maintain and change it, you rewrite it as often as necessary, you throw it away and do something else.
I’ll suggest an alternative meaning of “dead”: when it stops being used. By this definition, most Forth programs are dead. (duck) More seriously, it is abuse of privilege to claim some software is dead just because it’s hard to modify. If people are using it, it is providing value.
It is the fundamental property and fate of all software to outlive its creator. The mainstream approach, for all of its many problems, allows software to continue to serve its users long after the original authors leave the scene. They decay, yes, but in some halting, limping fashion they continue to work for a long time. It’s worth acknowledging the value of this longevity. Any serious attempt to replace mainstream software must design for longevity. That requires improving on our ability to comprehend each other’s creations. And Forth (just like Scheme and Prolog) doesn’t really improve things much here. Even though ‘understanding’ is mentioned above, it is in passing and clearly not a priority. Even insightful Forth programs can take long periods of work to appreciate. If a tree has value in the forest but nobody can appreciate it, does it really have value? I believe comprehensibility is the last missing piece that will help Forth take over the world. Though it may have to change beyond recognition in the process.
(This comment further develops themes I wrote about earlier this year. Lately I’ve been working on more ergonomic machine code, adding only the minimum syntax necessary to improve checking of programs, adding guardrails to help other programmers comprehend somebody else’s creation. Extremely rudimentary, highly speculative, very much experimental.)
I’ll suggest an alternative meaning of “dead”: when it stops being used. By this definition, most Forth programs are dead. (duck) More seriously, it is abuse of privilege to claim some software is dead just because it’s hard to modify. If people are using it, it is providing value.
We ought to distinguish dead-like-a-tree from dead-like-a-duck. A dead tree still stands there and you can probably even put a swing on it & use it for another 15-20 years, but it’s no longer changing in response to the weather. A dead duck isn’t useful for much of anything, and if you don’t eat it real quick or otherwise get rid of it, it’ll liable to stink up the whole place.
A piece of code that is actively used but no longer actively developed is dead-like-a-tree: it’s more or less safe but it has no capacity for regeneration or new growth, and if you make a hole in it, that hole is permanent. Once the termites come (once it ceases to fit current requirements or a major vulnerability is discovered) it becomes dead-like-a-duck: useless at best and probably also a liability.
I get that we’re supposed to keep original titles but I’d argue that the spacing is a result of the site’s “styling” and doesn’t have to be reproduced (just like we wouldn’t prepend a # to every submission’s title that links to a markdown doc)
Great article. I recommend reading yosefk’s article with it. I’m going to draw on it as I counter some of this one.
“due to its roots in simplicity and compactness, considering that it emerged in the microcomputer era, on machines that provided (for todays standards) severely limited capacity.”
“You step down on the level of assembly language which may sound daunting, yet gives you full control over every aspect of memory layout. There are no articial barriers between you and the machine’s architecture”
This is only partly true. It’s other root was in the personal preferences of Chuck Moore. That’s why it’s 18-bit, stack-oriented design in an era of 8/16/32/64-bit stack and register machines. An 18-bit stack on a 32-64-bit RISC machine is neither the simplest nor most efficient way to do things. Even if aiming for low gates, the billions a year in 8-16-bit MCU’s sold tell me there’s people getting by with less. The constructs themselves aren’t close to how we think about a lot of operations. Languages like LISP and Nim have metaprogramming that let us write constructs close to how we think that get turned into effecient primitives we still can understand. They have some benefits of Forth without the downside of putting square pegs into round holes.
So, Forth is the result of both squeezing what one can out of constrained hardware/software as author says and arbitrary decisions by Chuck Moore. It’s easier to understand some of it when you remember that one person just liked doing it that way, refused to adopt any method from others, and tried to force everything through his limited tools. He’s actually like a for-profit, industrial version of the demoscene. It’s cool seeing what he does. It’s also objectively worse than what others are doing along a lot of metrics. Especially hardware where his tools can’t handle modern, deep-sub-micron designs with their speed, size, cost, and energy benefits.
So, I default on thinking that future work in Forth should attempt to isolate and ditch arbitrary restrictions to build a similar model of simplicity for current CPU’s or hardware design. A modern CPU is 64-bits w/ registers and stacks, several levels of cache, SIMD, multicore, maybe hardware accelerators, supports memory isolation, and supports VM’s. What does the simplest implementation of that look like in software? For hardware, it has to be FSM’s that are synthesized and resynthesized in a series of NP-hard problems on manufacturing processes that have up to 2,500 design rules at 28nm. One problem even requires image recognition on circuits themselves before redrawing them. What does the simplest, EDA tool for that look like? Answering such questions gets us the benefits of Moore’s philosophy without the unnecessary baggage.
Hint: Microcode and PALcode. They’re like the ISA version of defining Forth functions. I want microcode I can customize with open tools in every CPU. :)
“Imagine being able to debug device drivers interactively”
This is an argument for interpreters, not Forth itself. Forth is one possibility. An interpreter for a subset of C or superset of assembly (eg Hyde’s HLA) w/ live debugging and code reloading is also possible. I’ve thought about building one many times. I’d be shocked if there weren’t already a dozen examples.
“You can’t have both a language that provides direct low-level access to the machine and that at the same time give you all advantages of a high-level environment like automatic memory management.”
These languages exist. They’re safe, systems languages that allow inline assembly. There’s also typed and macro assembly languages. Recently, there’s work on type systems to know combinations of them behave as expected. You can’t do just any arbitrary thing. You can mix approaches where you want, though.
“It is satisfying and reassuring to be able to fully understand the code that is generated from your sources”
Wirth showed you can do this with a language built for human understanding. His languages are simple enough that students design compilers for them. One could do an interpreter even easier. It’s pretty clear what object code will come out of it. And a little discipline can prevent things like cache misses. Worst case, you just look at the assembly of each module to see if you want to tinker with it. As with any other goal, it’s possible that Forth takes simplicity too far. I thought Wirth did that, too. I think there’s tradeoffs to be made between simplicity and complexity. Forth philosophy, like Wirth’s, will sacrifice everything else to simplicity. I’m fine with a more complex implementation of a language if it lets me express myself faster, safer, and so on with efficient code coming out.
“But why do so many insanely complex protocols and architectures exist?”
Author is right that a lot of complexity is unnecessary. The author also writes like all complexity is unnecessary. This is incorrect: much of it is intrinsic to the software requirements as Brooks wrote. Even a simple design becomes more complex if you handle stuff like error handling, software updates, backups, security mitigations, and so on. Even Forth itself is more complex than a native alternative with similar primitives and macros since it will have an interpreter/compiler/updater built in. Forth’s people think that complexity is justified over bare metal since it gives them benefits in the article. Well, that’s what we’re saying about a lot of this other complexity in CPU’s, OS’s, and languages.
“It means to drop the bells and whistles, the nice user interfaces”
User adoption, onboarding, and support is apparently not a thing in the Forth world. Both market share and usability studies showed GUI’s were superior to text for common applications for most users. So, we should give them what they want, do what works, or however you want to look at it. If we don’t need it, then don’t implement it to reduce complexity. There’s plenty of terminal users or services that don’t need GUI’s. That’s not them being inherently bad like author argues, though.
“What you reuse of a library is usually just a tiny part, but you pay for all the unused or pointless functionality and particularly for the bugs that are not yours.”
Author massively overstates real problems that come with bringing in dependencies. Lots of libraries are simple to use and easy to swap out. The OpenBSD and Suckless camps regularly crank out simple implementations of software. Even the complex ones like Nginx often have guides that make installing and using them simple. The complexity is dealt with by somebody else. Likewise with these hosting providers that make it simple to get some services running at almost no cost.
I imagine many businesses and products wouldn’t happen at the rate and cost they do if each developer spent tons of time rebuilding compatible networking stacks, web servers, and browsers from the ground up with deep understanding of them. Nah, they just install a package, read its guide, and Get Shit Done. You could say we opponents of Do It Yourself just like to Get Shit Done in Whatever Way Works. If you do API’s and modularity right, you can always improve later on anything you had to keep shoddy to meet constraints.
Forth was the first programming language I encountered. I’ve rarely used it (debugged from the openboot prom once, wrote some minor things in Quartus on the palm pilot) since learning it as a child in 1978. There’s a joke that if you’ve seen one Forth, you’ve seen one Forth. This rings true because there’s nearly nothing to Forth but a couple of primitives everyone more or less agrees upon, a standard everyone ignores, a philosophy, and the problem at hand. The solution is to build up the language and it becomes a DSL for that specific problem, as understood by that particular programmer, and becomes weird.
I really enjoyed this article: the author has clearly put a lot of thought into it.
In my mind — and I consider myself one — I would use the term ‘modernist’ to describe this way of thinking. The idea that there is enlightenment to be achieved through endless refinement and attention — that coding is precisely an art, something worth this incredible degree of attention despite losing any semblance of productivity.
Now I really do think of coding as an art too, and I wholly encourage the author to enjoy this artistic aspect of coding they’ve found! But under no circumstances should this be considered the only artistic avenue when it comes to code. Sure, there’s an art to creating the most minimal, featureless, compact, piece of forth, but there’s also an art to creating the very best and most intuitive user interface, and an art to jumping into a hundred thousand line code base and getting your bearings, an art to creating languages that do try to span the divide between close-to-the-hardware and woderfully-abstracted, even an art to being the most ‘productive’, hammering out the most number of features in a short amount of time.
Absolutely enjoy your minimalism — maybe I’ll join you! — but there are many ways to have fun programming, and all should be acceptable.
After spending a few months with Forth earlier this year, I absolutely agree that Forth can be extraordinarily simple and compact, that mainstream software is an endless brittle tower of abstractions, and that the aha moment when you find the right abstractions of a problem can be transcendent. But writings like this also indicate limitations that the Forth community unquestioningly accepts.
This is fine. However, the next step is not:
I’ll suggest an alternative meaning of “dead”: when it stops being used. By this definition, most Forth programs are dead. (duck) More seriously, it is abuse of privilege to claim some software is dead just because it’s hard to modify. If people are using it, it is providing value.
It is the fundamental property and fate of all software to outlive its creator. The mainstream approach, for all of its many problems, allows software to continue to serve its users long after the original authors leave the scene. They decay, yes, but in some halting, limping fashion they continue to work for a long time. It’s worth acknowledging the value of this longevity. Any serious attempt to replace mainstream software must design for longevity. That requires improving on our ability to comprehend each other’s creations. And Forth (just like Scheme and Prolog) doesn’t really improve things much here. Even though ‘understanding’ is mentioned above, it is in passing and clearly not a priority. Even insightful Forth programs can take long periods of work to appreciate. If a tree has value in the forest but nobody can appreciate it, does it really have value? I believe comprehensibility is the last missing piece that will help Forth take over the world. Though it may have to change beyond recognition in the process.
(This comment further develops themes I wrote about earlier this year. Lately I’ve been working on more ergonomic machine code, adding only the minimum syntax necessary to improve checking of programs, adding guardrails to help other programmers comprehend somebody else’s creation. Extremely rudimentary, highly speculative, very much experimental.)
We ought to distinguish dead-like-a-tree from dead-like-a-duck. A dead tree still stands there and you can probably even put a swing on it & use it for another 15-20 years, but it’s no longer changing in response to the weather. A dead duck isn’t useful for much of anything, and if you don’t eat it real quick or otherwise get rid of it, it’ll liable to stink up the whole place.
A piece of code that is actively used but no longer actively developed is dead-like-a-tree: it’s more or less safe but it has no capacity for regeneration or new growth, and if you make a hole in it, that hole is permanent. Once the termites come (once it ceases to fit current requirements or a major vulnerability is discovered) it becomes dead-like-a-duck: useless at best and probably also a liability.
[Comment removed by author]
I get that we’re supposed to keep original titles but I’d argue that the spacing is a result of the site’s “styling” and doesn’t have to be reproduced (just like we wouldn’t prepend a
#
to every submission’s title that links to a markdown doc)In Swedish typography this is called “spärrning”, from the German Sperrsatz.
https://en.wikipedia.org/wiki/Emphasis_(typography)#Letter_spacing
It’s basically the only emphasis you have available if you can’t underline or use italics/bold.
Yeah, I agree. The spaces just make things like searching for the article harder.