🦾 Writing Anteforth, a Forth-like in SPARK
#DesmetC (signed char -> long) promotion is now working on my branch: got it on the first try, which hopefully means I'm internalizing the codebase. Now I will start on tests for mixed-sign arithmetic.
It's been a pretty productive night in the ol' #DesmetC codebase. Regression tests finally checked in, all the mixed-size integer addition/subtraction involving signed chars I could think of is exercised and passing, nice.
Then I try i8 * i8 -> int and it instantly breaks, not so nice 🫠.
Oh well, that gives me something to fix tomorrow.
Also, I haven't ventured into floating point conversion land yet, either. I'm sure that'll have plenty of dragons when used with signed char.
All this is making me appreciate the wisdom of BCPL and B who have just a word-sized type -- or #Forth which takes that and adds char, as a treat.
In the Spring of 2025, I taught a course about interactive programming environments to a small group of students. For various reasons, I have to take down the official web page for that site, which includes slides that may be of interest to people who care about these things. I have therefore chosen to publish those slides on Lost Bits under a Creative Commons License.
https://lostbits.net/blog/interactive-programming-environments.html
If you are interested in such things, check it out!
#lisp #smalltalk #squeak #forth #emacs
In the Spring of 2025, I taught a course about interactive programming environments to a small group of students. For various reasons, I have to take down the official web page for that site, which includes slides that may be of interest to people who care about these things. I have therefore chosen to publish those slides on Lost Bits under a Creative Commons License.
https://lostbits.net/blog/interactive-programming-environments.html
If you are interested in such things, check it out!
#lisp #smalltalk #squeak #forth #emacs
The best example of distilled software that comes to mind is Project #Oberon, which was distilled by Niklaus Wirth (and others) for most of Wirth's lifetime (if you count his earlier time working on Pascal and Modula as earlier steps of the distillation). https://projectoberon.net/
There are also #Forth and #Lisp, of course, but they've been distilled in many different directions by many people so there isn't a clear unifying idea. You have to get more specific. Now, Chuck Moore's evolution of Forth -> MachineForth -> ColorForth certainly counts as distillation.
#LuaLang also comes to mind. Porting the most modern Lua to the 188K TI-92+ calculator (last year) is what sold me on the idea that widely used modern software can remain useful on the oldest computers. That said, Lua is not entirely immune to bloat: I had to roll back from v5.4 to v5.2 to cut my memory usage from ~170K to ~128K 😉
principles of software distillation:
Old software is usually small and new software is usually large. A distilled program can be old or new, but is always small, and is powerful by its choice of ideas, not its implementation size.
A distilled program has the conciseness of an initial version and the refinement of a final version.
A distilled program is a finished work, but remains hackable due to its small size, allowing it to serve as the starting point for new works.
Many people write programs, but few stick with a program long enough to distill it.
The best example of distilled software that comes to mind is Project #Oberon, which was distilled by Niklaus Wirth (and others) for most of Wirth's lifetime (if you count his earlier time working on Pascal and Modula as earlier steps of the distillation). https://projectoberon.net/
There are also #Forth and #Lisp, of course, but they've been distilled in many different directions by many people so there isn't a clear unifying idea. You have to get more specific. Now, Chuck Moore's evolution of Forth -> MachineForth -> ColorForth certainly counts as distillation.
#LuaLang also comes to mind. Porting the most modern Lua to the 188K TI-92+ calculator (last year) is what sold me on the idea that widely used modern software can remain useful on the oldest computers. That said, Lua is not entirely immune to bloat: I had to roll back from v5.4 to v5.2 to cut my memory usage from ~170K to ~128K 😉
While I was working on this, the article Python Numbers Every Programmer Should Know appeared on the orange website. In #LuaLang, and on a 16-bit target, these overheads are less -- for example, a number weighs 10 bytes instead of 24 bytes -- but overheads don't have much place to hide on a small, slow machine.
(Btw numbers cost 7 bytes each in 8-bit Microsoft BASIC so Lua isn't gratuitously inefficient here, even by the standards of 50 years ago.)
One place that makes overhead really obvious: a 64K segment holds a table of length, at most, 4,096 entries. That's 40,960 bytes, and Lua's strategy is to double allocation size every time it wants to grow the table. 2 x 40,960 exceeds a 64K segment, so 4,096 entries is the growth limit.
On a 640K machine, after deducting the ~250K (!) size of the interpreter (which is also fully loaded into RAM), you'll get maybe five full segments free if you're lucky. So that's like maybe 20,000 datums total, split across five tables.
Meanwhile a tiny-model #Forth / assembly / C program could handle 20,000 datums in a single segment without breaking too much of a sweat!
The efficiency has costs to programmer time, of course. Worrying about data types, limits, overflows, etc. The kinds of things I was hoping to avoid by using Lua on this hardware -- and to its credit, it does a good job insulating me from them. Its cost is that programs must be rewritten for speed in some other language once out of the rapid prototyping phase and having reasonable speed / data capacity becomes important.
I'd estimate the threshold where traditional interpreters like Lua become okay for finished/polished software of any significant scope, is somewhere around 2MB RAM / 16MHz. So think, like, a base model 386. Maybe this is why the bulk of interpreters available in DOS are via DJGPP which requires a 386 or better anyway.
#BASIC was of course used on much smaller hardware, but was famously unsuited to speed or to large programs / data.
I know success stories for #Lisp in kilobytes of memory, but I'm not quite sure how they do it / to what extent the size of the interpreter, and overhead of data representation (tags + cons representation), eats into available memory and limits the scope of the program, as seen with other traditional interpreters.
This is beginning to explain why #Forth has such a niche on small systems. It has damn near zero size overhead on data structures. (The only overhead is for the interpreter core (a few K) and storing string names in the dictionary (which can be eliminated via various tricks)). ~1x size and ~10x speed overhead is the bargain of the century to unlock #repl based development. However, you're still stuck with the agonizing pain of manual memory management and numeric range problems / overflows. Which is probably why the world didn't stop with Forth, but continued on to bigger interpreters.
By 100x speed difference, I mean the uu encoding/decoding rate is about 30 bytes per second. I'm not accustomed to a correct program being this catastrophically slow ;)
Not throwing shade at #LuaLang for the 100x speed difference: it's astonishing that a modern interpreter can be built for a 4.77 MHz 8088 and run at usable, if lukewarm, speeds. The 100x size difference comes down to the interpreter including Lua's full library, most of which isn't needed for all programs.
If I had to guess, I'd expect most of the time to be spent in string operations and syscalls. Lua translates file contents to (immutable) string when reading, so more conversions are necessary to perform transformations and output results. Moreover, when writing output the program does f:write() 3-4 bytes at a time: if this were unbuffered and translating directly to hundreds of write syscalls, that would also be very slow.
While I was working on this, the article Python Numbers Every Programmer Should Know appeared on the orange website. In #LuaLang, and on a 16-bit target, these overheads are less -- for example, a number weighs 10 bytes instead of 24 bytes -- but overheads don't have much place to hide on a small, slow machine.
(Btw numbers cost 7 bytes each in 8-bit Microsoft BASIC so Lua isn't gratuitously inefficient here, even by the standards of 50 years ago.)
One place that makes overhead really obvious: a 64K segment holds a table of length, at most, 4,096 entries. That's 40,960 bytes, and Lua's strategy is to double allocation size every time it wants to grow the table. 2 x 40,960 exceeds a 64K segment, so 4,096 entries is the growth limit.
On a 640K machine, after deducting the ~250K (!) size of the interpreter (which is also fully loaded into RAM), you'll get maybe five full segments free if you're lucky. So that's like maybe 20,000 datums total, split across five tables.
Meanwhile a tiny-model #Forth / assembly / C program could handle 20,000 datums in a single segment without breaking too much of a sweat!
The efficiency has costs to programmer time, of course. Worrying about data types, limits, overflows, etc. The kinds of things I was hoping to avoid by using Lua on this hardware -- and to its credit, it does a good job insulating me from them. Its cost is that programs must be rewritten for speed in some other language once out of the rapid prototyping phase and having reasonable speed / data capacity becomes important.
I'd estimate the threshold where traditional interpreters like Lua become okay for finished/polished software of any significant scope, is somewhere around 2MB RAM / 16MHz. So think, like, a base model 386. Maybe this is why the bulk of interpreters available in DOS are via DJGPP which requires a 386 or better anyway.
#BASIC was of course used on much smaller hardware, but was famously unsuited to speed or to large programs / data.
I know success stories for #Lisp in kilobytes of memory, but I'm not quite sure how they do it / to what extent the size of the interpreter, and overhead of data representation (tags + cons representation), eats into available memory and limits the scope of the program, as seen with other traditional interpreters.
This is beginning to explain why #Forth has such a niche on small systems. It has damn near zero size overhead on data structures. (The only overhead is for the interpreter core (a few K) and storing string names in the dictionary (which can be eliminated via various tricks)). ~1x size and ~10x speed overhead is the bargain of the century to unlock #repl based development. However, you're still stuck with the agonizing pain of manual memory management and numeric range problems / overflows. Which is probably why the world didn't stop with Forth, but continued on to bigger interpreters.
My overnight activity on New Year's Eve was to rewrite the #uuencode utility that I lost in a battery-exhaustion incident. The old version was in #Forth, the new version in #LuaLang. Including interpreter size, the Lua version is 100x larger and 100x slower. I was not intending to provide a case study upholding Jeff Fox's writings about Forth efficiency, but there you go.
👾 Romforth: Ultra Portable, Small, Baremetal Forth for various processors
「 So far, it has been ported to the following instruction sets: x86 (16-bit, 32-bit and 64-bit), PDP11, 68000, SPARC, Z80, MSP430, ARM64, RISC-V(rv32, rv64), WASM, Padauk, 6502, 8051, 6809, IBM 1130 」
ual v0.7.3 released
ual is a systems language for orchestration and embedded computation. It comes in two forms: a compiler targeting native binaries, and an interpreter for development. Both share the same runtime and produce identical results. This release marks the completion of runtime unification — the interpreter now has full parity with the compiler, including true goroutine-based concurrency, using Go as IR and bootstrapping tool.
What makes ual different
ual is built on a specific philosophical foundation: that coordination is the primary problem of programming and that types and computation are subordinate within coordinated contexts. Data lives in typed stacks accessed through perspectives (LIFO, FIFO, Indexed, Hash). Time is explicit — blocking waits, timeouts are native. Errors must be acknowledged before proceeding.
Binaries available for:
Linux (amd64, arm64, arm, 386), macOS (Intel, Apple Silicon), Windows (64-bit, 32-bit), FreeBSD, OpenBSD, NetBSD
Release:
https://github.com/ha1tch/ual/releases/tag/v0.7.3
Docs
https://github.com/ha1tch/ual/blob/main/docs/MANUAL.md
#ual #golang #foss #rust #rustlang #forth #programming #compsci
An Attempt at a Compelling Articulation of Forth's Practical Strengths and Eter
https://im-just-lee.ing/forth-why-cb234c03.txt
#HackerNews #Forth #Programming #Articulation #Practical #Strengths #HackerNews
ual v0.7.3 released
ual is a systems language for orchestration and embedded computation. It comes in two forms: a compiler targeting native binaries, and an interpreter for development. Both share the same runtime and produce identical results. This release marks the completion of runtime unification — the interpreter now has full parity with the compiler, including true goroutine-based concurrency, using Go as IR and bootstrapping tool.
What makes ual different
ual is built on a specific philosophical foundation: that coordination is the primary problem of programming and that types and computation are subordinate within coordinated contexts. Data lives in typed stacks accessed through perspectives (LIFO, FIFO, Indexed, Hash). Time is explicit — blocking waits, timeouts are native. Errors must be acknowledged before proceeding.
Binaries available for:
Linux (amd64, arm64, arm, 386), macOS (Intel, Apple Silicon), Windows (64-bit, 32-bit), FreeBSD, OpenBSD, NetBSD
Release:
https://github.com/ha1tch/ual/releases/tag/v0.7.3
Docs
https://github.com/ha1tch/ual/blob/main/docs/MANUAL.md
#ual #golang #foss #rust #rustlang #forth #programming #compsci
Very much enjoyed (finally) reading this today (while procrastinating from other things I was supposed to be doing). The paper is in PDF form, here:
https://akkartik.name/akkartik-convivial-20200607.pdf
Title is "Bicycles for the Mind Have to Be See-Through".
Anyone with a passing interest in #lisp, #forth, or #smalltalk has some very interesting material to chew on. And the @malleablesys people too, but I presume they've seen it over on the forum, where I first became aware of it myself.
The idea as I understood it is, to quote the paper, to "prioritize comprehension over ease of authorship". And bring that to the whole software stack, and see how far you can push it.
@akkartik where is the whole project at now, how is it all going? I only eyeballed the source code, but the rationale behind the whole thing was very interesting.
One more quote from the end:
"Creating an entire new stack may seem like tilting at windmills, but the mainstream Software-Industrial Complex suffers from obvious defects even in the eyes of those who don’t share our philosophy."
Very much enjoyed (finally) reading this today (while procrastinating from other things I was supposed to be doing). The paper is in PDF form, here:
https://akkartik.name/akkartik-convivial-20200607.pdf
Title is "Bicycles for the Mind Have to Be See-Through".
Anyone with a passing interest in #lisp, #forth, or #smalltalk has some very interesting material to chew on. And the @malleablesys people too, but I presume they've seen it over on the forum, where I first became aware of it myself.
The idea as I understood it is, to quote the paper, to "prioritize comprehension over ease of authorship". And bring that to the whole software stack, and see how far you can push it.
@akkartik where is the whole project at now, how is it all going? I only eyeballed the source code, but the rationale behind the whole thing was very interesting.
One more quote from the end:
"Creating an entire new stack may seem like tilting at windmills, but the mainstream Software-Industrial Complex suffers from obvious defects even in the eyes of those who don’t share our philosophy."
The utopias of yesterday – GfZK Leipzig presents the history of Robotron...
(Robotron was East Germany's largest computer manufacturer and they also made the machine I first got to use on my way to becoming a "computer toucher"... Almost exactly 10 years ago, I wrote about those early experiences and the lessons learned already back then in this long read article here:
https://medium.com/@thi.ng/the-jacob-s-ladder-of-coding-4b12477a26c1
Exhibition info (via @aramba):
https://archive.ph/UWFgT
#Exhibition #RetroComputing #ComputerHistory #Robotron #Atari #Arduino #STM32 #Forth #Assembly #Demoscene