Friday, June 27, 2008
The other day, I realized that there wasn't a great place to discuss Lisp that was using a post-Y2K sort of interface. Sure, comp.lang.lisp has been around forever, and through Google Groups it has an HTTP interface, but when you post to Usenet you become an instant spam magnet. And new users think an all text-mode interface with no markup is just so 1985. And yes, there's always #lisp on Freenode.net IRC, which is great for realtime, but bad for searching for coherent answers.
So, I created Lisp Forum: www.lispforum.com
The content is a bit sparse right now. That's where you come in. Please register, hang out, and participate. My goal is to make this a friendly place with a bit less Smug Lisp Weenie-ness than comp.lang.lisp, suitable for both newcomers and old pros.
The other day, I discussed languages which I thought were timeless. Among them, I listed Lisp, C, and Forth. After writing that posting, I spent some time playing with Forth again. Today, while browsing Reddit, I stumbled on this interview with Charles Moore, the creator of Forth.
Forth is one of those languages, like Lisp, that I'd recommend that everybody study at least for a time. Even if you don't walk away believing it's the Language to End All Languages, you'll be better off for the experience. In fact, Forth shares a lot of fundamental attributes with Lisp while at the same time appearing almost completely different to a programmer writing code.
Some of the shared attributes include:
- Forth and Lisp both erupt from a very small nucleus of fundamental constructs. As Alan Kay has described Lisp as being "Maxwell's equations of sofware," similar statements would also apply to Forth. Both Forth and Lisp are fundamentally simple as a result. Sure, the libraries could be huge, but learning the actual language itself, the core rules, requires no more than a few minutes for each language. With both languages, there is a set of advanced rules (things like macros for Lisp or compiling words for Forth), but the basics are trivial.
- Forth and Lisp are duals of each other when it comes to their syntax. In both cases, the programmer is essentially handing the system a direct representation of the parse tree. The parsers for each language are trivial. Lisp uses prefix notation, whereas Forth uses postfix: "(+ 1 2)" vs. "1 2 +" for example. The use of prefix notation semi-requires delimiters to be inserted, giving us Lisp's beloved/hated parenthesis. With Forth, all computation revolves around the stack. Because the operation always occurs after the parameters are pushed, you don't need the delimiters. Words simply consume whatever parameters from the stack that they want to. One implication of this, however, is that you can do things like "(+ 1 2 3 4)" in Lisp. In Forth, this ends up being either "1 2 + 3 + 4 +" or "4 3 1 2 + + +".
- Forth and Lisp are both extensible. When you create a new function or macro in Lisp, you're extending the language itself. Your new function or macro is a peer with everything else in the language, not a red-headed stepchild. Similarly, with Forth, a word is a word is a word. You can create Forth words that interact with the compiler and do all sorts of crazy stuff. Most Forth systems also include an assembler so you can create high-performance, primitive Forth words as well.
- Forth and Lisp are both interactive. They both use a REPL. Forth doesn't call it that, but that's what it is. The benefits of this are similar in both. You tend to code a little, then test a little, then code a little, then test a little. In the interview with Moore linked above, you can see where he talks about the speed at which things got developed as a result of the interactivity of his Forth system.
- Forth and Lisp both include a compiler. I guess this really isn't a fundamental attribute of Lisp itself (you could be fully interpreted), but most Lisp systems do have a compiler. In some cases, that compiler can be pretty simple and primitive. In other cases, it could be very sophisticated (CMUCL/SBCL). With Forth, the compiler for threaded code is both fundamental and at the same time trivial. More sophisticated Forth systems can create more complex compilers (subroutine threading, superoperations, etc.), but those are not required.
All that said, Forth and Lisp are also very different:
- Typically, Forth operates at the machine level, with very direct exposed representations for objects. Forth programmers think in terms of machine words, bits and bytes. In some parts of a given program, a given bit pattern will represent a character or pointer or whatever, but from Forth's point of view they're all just bit patterns in a machine word. In contrast, Lisp programmers operate a higher levels of representation with first-class status for things like symbols, numbers, characters, etc.
- As a result of this "level difference," Forth programs are more memory efficient than Lisp programs, but they're also more dependent on the underlying machine fundamentals. For instance, if you changed the word size of the machine, a Lisp programmer probably wouldn't be aware of it. A Forth programmer might have to scramble to rewrite a good portion of the code. But very useful Forth programs are measured in KB of size, not MB.
- A big difference that typifies the issue of operating at the machine level vs. higher levels of abstraction is that Forth doesn't include any GC capabilities. All memory management must be done manually by the programmer.
- Forth isn't very tolerant of program bugs. Because you're operating at the machine level, when things go wrong, you might end up with a crashed machine. The Forth response to that is to just push the reset button and reload the system. Because the compiler is so fast, you'll be back to where you were before the crash in no-time. In contrast, Lisp makes a lot of effort to land the programmer in an interactive debugging shell when it detects an error condition.
Note that people have proposed systems which bridge between the two worlds. Factor is basically a Forth stack machine and syntax, augmented with high-level, Lisp-like data types and a GC. The result is a system which delivers Forth-like syntax with a Lisp-like debugging and development environment. Depending on your point of view, you'll either think this is the best of both worlds or the worst.
For me personally, I like both Forth and Lisp, but I'd use them in completely separate domains. If I was working on a deeply embedded project, where I'd want to be close to the machine architecture and where I had only a few KB into which to implement the program, I'd choose Forth. If I was writing a large application that would be running on a server with GBs of memory, I'd choose Lisp. Each works well within its target domain and the advantages of each are nearly the same: a small, extensible language with an iterative, interactive development environment.
As for systems like Factor, for me it's a "tweener" that doesn't fit my needs. By getting away from the machine details, adding high-level data types, GC, etc., Factor necessarily pushes itself out of the embedded world. You simply won't have microcontrollers running Factor. And if I'm going to be running on a system with an underlying operating system, a large graphical display, and GB of memory, I'd rather do my development in Lisp. While I like Forth, I find that Lisp's sexpr notation more closely matches my thinking model. With Forth's implicit stack, I have to be thinking all the time about what data values are at what positions on the stack. I'd choose to deal with that for an embedded design to get all the other attributes of Forth in that environment, but with fewer constraints, I'd choose Lisp over a "Forth with high-level data types and GC" like Factor.
Now, that's just me. For you, Factor may be the ticket. Slava Pestov is not an idiot (and you can quote me on that, Slava). As Factor's creator, he has obviously built a system that works well for him. Other people who seem to have far better programming skills than I do are working with Factor, too. The development environment they have put together seems to have borrowed a lot of ideas from Lisp machines, and I could see the Factor environment being really productive.
Whatever you choose, realize that all of these languages have some things they share. And fundamentally, both Lisp and Forth are timeless.
Wednesday, June 04, 2008
I started thinking about it today. What if I was stuck on the proverbial desert island? What programming language would I want to have to hammer out that critical calculation that would somehow return me back to civilization?
I know this is heresy, but I think I'd choose C.
"WTF!?!?!" I hear you cry. "This is a Lisp blog, dammit. You can't choose C." Well, you're right. But let me ramble for a second.
In my thinking this afternoon, I started to consider which programming languages are really timeless? Not just popular, mind you, but timeless. There aren't that many. COBOL was popular once. It wasn't timeless. PERL is certainly popular, but given the changes happening in PERL 6, it's not timeless either. Python? A very fun language, but not timeless. Ruby? Very hip and cool, but not timeless. How about Java? I once read something from the Long Now folks saying that they would be using Java for any programming because it was a language that could stand the test of time because of WORA. (If somebody can point me to a reference for this, please do. I was Googling madly for it this afternoon, but I can't seem to find it. I believe it was something Bill Joy wrote.) I didn't believe it then, and I certainly don't believe it now.
Nope, there is a huge difference between popularity and timelessness. I think C is timeless. C has been called "high level assembler." There are few other languages that do such a great job of raising you just slightly out of the world of bits, bytes, and machine registers, just enough to give you high level conditionals and loops, but not far enough that you can't get back to machine words.
Lisp is also timeless. Alan Kay has said that Lisp is "Maxwell's Equations of software." He's right. There is something that is so fundamental about Lisp that it simply cannot go away. It's far more than the parenthesis or the lists or the macros or any of that stuff that Lispers love. It's more than Common Lisp or Scheme or MacLisp or Emacs Lisp. It's all that stuff and more. At its core, Lisp is so compact and powerful as to be timeless. From McCarthy's original paper springs forth the whole world of every program that can be written.
In fairness to Kay, Smalltalk is pretty close to being timeless, too. In a certain sense, Smalltalk lives on in Ruby, but it's really the idea of message passing rather than Smalltalk specifically. Insofar as Smalltalk is synonymous with message passing (which it really isn't), it's timeless.
FORTH is timeless. The basics of a stack machine are pretty fundamental. The various FORTH words and implementation techniques may change (c.f. Factor), but the fundamentals are still recognizable as FORTH.
And that's why I'd choose C as my desert island language. Because in less than 10 pages of C, I can bootstrap a basic Lisp interpreter (or FORTH interpreter, for that matter). And with Lisp, I can write everything else. When PERL and Python are as dated as COBOL and Pascal, we'll still have Lisp. And it'll still be very recognizable as Lisp.