It’s now two years since I began my Riscyforth project of writing a Forth for RISC-V single board computers. But it wasn’t until yesterday that I wrote my first serious/useful (if you like this sort of thing) program for it, a version of Conway’s Game of Life.
(This program wasn’t as long as the unit test suite I wrote before, but it is more useful, I’d argue.)
Why did it take so long to do something ‘useful’? Mainly because I didn’t start writing code that allowed screen manipulation (running on top of the nCurses library) making something other than simple textual output possible (thoug there is still no graphics support). All the other time has been spent on writing the code that implements the core system, support for doubles (which in Forth means double length – here 128 bit – integers) and then floating point code.
The first three months were spent getting my head around the basic concepts which I took from this book – which is out of print and hard to get: I certainly wouldn’t pay the prices being sought on Amazon – but also available online: it’s an excellent introduction to the concepts, if very much written for the 8-bit world of the late 1970s, early 1980s.
Then came a lot of coding. I had decided to write it all in RISC-V assembly because I grew up at a time when all Forth’s were implemented that way for speed – it became obviously quickly that a C implementation wouldn’t be much (any?) slower and probably a lot easier to write, but I have (re-)learnt a lot about assembly as a result so from a personal perspective it’s not been a waste of time. (There is some C in Riscyforth now, it was just too complex to write some of it in assembly and keep my sanity and/or produce efficient code, though the vast, vast majority remains as assembly).
Forth is not exactly a growing language these days (when I told my brother I was doing this he just laughed at how mad it all seemed), and the documentation is very much written by and for people who have been around it for a long time, with all the associated assumptions of shared knowledge and practise that is not ever (in my opinion, anyway) fully and concisely stated that one often sees with such hyper-mature projects.
It’s not that stuff is kept secret, but more than once I found myself having made design choices that ran into trouble when other subtlties of the language crept up. Some hetrodox design decisions (like using 64 bit integers throughout rather than the standard 32 bit) fit well with the language though and were never in doubt.
Now it’s done I am very pleased with it and I also like how fast it is – the program is ‘compiled’ but not to a binary and Forth is somewhere between a scripting langauge and a truly compiled programming tool – hence the term ‘threaded interpreted language’ – but it certainly runs faster than the BASIC interpreter/DSL I wrote in Groovy a decade ago.
Writing the program – which didn’t take long once I’d got enough Curses support – did make me think quite a bit about the things we take for granted in other languages: principally about the concept of a variable.
If you had asked me last week to describe the fuction of a variable to starting programmer I probably would have used some analogy like a bucket into which we thow a value – but that’s really not true for C and most other languages.
In C we can have this:
int x; int y; x = 3; y = x * 2;
The thing is that here
x isn’t a bucket into which
3 is thrown – it is, essentially,
But this isn’t true in Forth, where the equivalent code is:
variable x variable y 0 x ! x @ 2 * y !
The declaration and assignment (
0 x !) parts are broadly analogous (though Forth is much less strongly typed than even C), but the subsequent access is crucially different. If that final line in Forth had read
x 2 * y ! we would be assigning the memory address of
x (doubled) to
@ (FETCH) operator looks like a small change but psycologically it is huge and the failure to do it properly and consistently was the biggest problem I had writing the code.
It also comes with the realisation that C variables and C++ references are much more closely related creatures than I had though – because both are automatically dereferenced pointers. My experience here has led me to suspect the difference between a pointer and this sort of ‘reference’ is at the root of a lot of people’s troubles with mathematical abstraction – because if even an experienced programmer can stumble over this sort of stuff then why we would we not expect many children (but not all, obviously) to struggle with similar concepts when they are introduced in school mathematics?
(But I’m not qualified to offer a solution to this problem – or even to assert all that strongly that I’m correct, it’s just a passing observation.)
And what now?
I have enjoied the two years working on Riscyforth but I also have to be honest – I appear to be the world’s only user of it (it is cloned a lot on github but I’ve never had a report from a user or a submitted patch).
I suspect there are better projects to put the effort into, though as of yet I don’t know what and so development may go on for a little while yet.