On the occasion of today being the “Forth”, I’ve translated the first of the prime finder benchmarks to run with the latest MacForth. My Forth is a bit rusty, and I didn’t have all the 64-bit numeric operators I needed, so the code is limited to 32-bit integers. Even with that shortcut and the fact that memory allocation is simpler than the other languages, the time for this test is not quite as fast as Java and optimized C++. The Forth code (snippet below—can’t imagine anyone would be interested in more than that) takes 14.1 seconds to find all the primes less than 10 million, while Java and Xcode C++ take about 11 seconds (C# and CodeWarrior C++ are around 40 seconds).
I can’t do justice to Forth here for the unfamiliar, but it had a lot going for it in its day and embraced some important programming concepts. It was Leo Brodie’s Thinking FORTH book (1984) where I first read about factoring code. There’s a whole chapter on factoring and iterative factoring (I guess that’s refactoring). I glad to see Forth gets some credit on the Wikipedia entry on refactoring.
In Forth, since each syntactical construct, including operators, is a word just like any other and with a stack to facilitate anonymous temporaries, (re)factoring can take place at all levels. Say you want to return f(A,B) or f(C,D) based on some condition. In Forth, you can use the condition to determine whether to push A and B on the stack or to push C and D on the stack, and then invoke f() outside of the conditional. Since operators are the same as any other word, the same would apply if you replace the function call with a divide operator, for instance—it could be factored out the same way.
Another key feature of Forth is the ability to extend the language itself so that you can write code in the problem domain. As an example of the extensibility, early Forths had no switch statements or even named local variables, but those features could be added as needed (and were). I’m reminded of this Forth feature when reading the Meta-Programming System work from JetBrains.
structure PrimeLinkStruct
long: +prime
long: +next
structure.end
0 value firstPrime \ addr of first PrimeLinkStruct
0 value latestPrime \ addr of latest PrimeLinkStruct
0 value primeCount
: newPrimeLink ( n -- addr )
here PrimeLinkStruct allot
tuck +prime !
0 over +next ! ;
: addPrime ( n -- )
newPrimeLink dup
latestPrime IF
latestPrime +next !
ELSE
to firstPrime
THEN
to latestPrime
1 +to primeCount ;