Hmm…

When checking out recent Lisp books, I realized that one of the reasons that makes SICP palatable is the fact that it’s “old”. I mean that in a good way. It predates Java, Python, Ruby, etc, and as a result, it does not adopt a smug attitude like, “Lisp is so much better than all these imitators that stole features from it but lack the essence”. Modern Lisp texts often seem to have a hard time keeping this attitude at bay, which makes for unpleasant reading when you’re coming from a different language (which is almost always the case).

Then again, from what I have seen, this attitude is less prevalent in the Scheme community…

:: Comments (1)

eBay listings, part 1

Well, I’ve started to sell some stuff on eBay… I will post the listings here, so you, my faithful readers, will get to see them first. :-)

I am actually selling these items so I can pay Dreamhost for another year of hosting, so it’s for a good cause. (I hope…)

Anyway, here are the first listings:

Programming Python, and the Cookbook, are a bit dated, of course… but that’s why they start out cheap! ;-)

The other two seem to be collectors items, more or less; my buy-it-now price is still lower than what you pay for the used version on Amazon, though.

More books coming up… mostly programming/computer science, and maybe some gaming stuff… if you are looking for something specific, and you suspect I might have it, drop me a note. :-)

:: Comments (4)

The icicle melts

For the past 2-3 months or so, I’ve been tinkering with a Lisp dialect called Liquid. Current prototype is in Python, but the idea is that I will eventually port this to a different platform; maybe the JVM, .NET, or Actionscript. (I should probably write a separate post discussing these choices.)

The prototype works, to some extent, and can be played with, for those who like this kind of stuff. (Just run the liquid script to get a REPL… error handling etc isn’t too good, but this should be better in future versions.)

Constructive criticism and ideas welcome (but keep in mind that this is just a prototype and that I am not an old Lisp hand :-).

:: Comments (3)

A dollop of Lisp

I’ve written a few (toy) Lisp interpreters over the years. Who hasn’t right? :-) At the core of such interpreters, there’s the eval-apply cycle. It basically works like this:

  • To evaluate an expression, call eval on it.
  • If the expression is atomic, this is just a matter of a simple name lookup, or the expression evaluates to itself (in the case of literals).
  • If the expression is composite (i.e. a list), it is considered to be a function call, where the first element of the list is a function, and the other elements are the arguments passed to that function. We evaluate all of these elements (again by calling eval on them), then call apply to do the actual function application.
  • If said function is built-in, we just call it, get a result back, and be done. If it’s a user-defined function, then we need to evaluate the expressions in that function’s body, using the procedure described here.

This is a simplified version — it doesn’t take into account special forms or macros, for example — but it does capture the essence of the eval-apply cycle.

Anyway, since expressions can be nested arbitrarily deep, it should be clear that eval and apply can and often do call themselves and each other.

Now, when writing a Scheme interpreter in a language like Python, it is tempting to use a straightforward translation of this principle. You write functions (or methods, if you wish) eval() and apply(), which may call themselves and each other. This works rather well — until you blow the stack. The implementation language’s stack, that is… because every call to eval or apply in the Scheme interpreter, also uses the implementation language’s call stack.

This is a problem, especially in Scheme, which relies on tail recursion to define “iterative” processes. (These still use recursion, but because of tail call optimization, they operate in constant stack space. See SICP 1.2.1.)

We can simulate tail recursion by using exceptions (as demonstrated by this Cookbook recipe, for example). This is the approach I followed in most of my projects. While it does work, sooner or later you’ll run into other problems, because the interpreter is still piggybacking on Python’s call stack, rather than having one of its own. So, implementing continuations, for example, becomes very difficult, as the notion of “the calling expression” is implicit.

So, I decided to use a different approach. And now we’re finally getting to the point of this blog post. :-)

This time I started with a call stack. The expression we want to evaluate is pushed onto it. For example,

(+ 1 a)

Then, I wrote a method run() which does one “evaluation step” using this call stack. If it’s done evaluating, it returns the result value, otherwise it returns None, as an indication that there’s still more work to do. (So, in order to evaluate an expression, you call it multiple times until you get a result back.)

How does such an evaluation step work? We take the topmost expression on the call stack, and start evaluating it. In this case, it’s a composite expression, so we just start at the beginning. We want to evaluate the symbol +. To do so, we push + onto the stack, and put a placeholder in its original position, indicated by “$$”.

;; call stack now looks like:
($$ 1 a) +

We’re now done with this step. In the next step, we again look at the topmost expression on the stack. It’s a symbol; we look it up, get a function back (represented by <lambda:+>, and stick it back into the original expression. We then move on to the next element of the list, which is 1, and push it.

(<lambda:+> $$ a) 1

Another step, and we get:

(<lambda:+> 1 $$) a

Let’s say the variable a has been defined and its value is 4. We then get:

(<lambda:+> 1 4)

Now we’re done evaluating the elements of this expression, and we can do the actual function application. Finally, we get the result 5.

This gets more complex when we use nested expressions, but the principle is the same.

(+ (* a (- c d)))
($$ (* a (- c d))) +
(<lambda:+> $$) (* a (- c d))
(<lambda:+> $$) ($$ a (- c d)) *
(<lambda:+> $$) (<lambda:*> $$ (- c d)) a
(<lambda:+> $$) (<lambda:*> 4 $$) (- c d)
;; ...etc...

It becomes more interesting when we introduce special forms into the mix. But since we have an explicit call stack now, we can clearly see what is going on with it, and tail recursion starts making much more sense. For example, if our topmost expression is

(if condition (do-this) (do-that))

and during evaluation we determine that condition evaluates to true, we can at that point replace the whole expression by

(do-this)

rather than having to push (do-this) and then sticking the result back into the if expression. Ditto for the last expression inside a begin block, and so on.

The special forms require a bit of hackery, since we don’t evaluate all of their arguments… but there’s only a few of them.

The proof-of-concept implementation that uses this concept is called dollop and is available at github. (Requires Python 3.0.) The name is because it’s only a “dollop of Lisp” (or rather, Scheme); it only supports a few special forms (begin, define, if, lambda), and a few functions for example programs (+, -, *, =, list). It cuts corners in other ways as well, as my goal was to get a working proof-of-concept out, not to write a complete Scheme interpreter.

Anyway, comments, bug fixes, improvements, etc, welcome.

(I will also release two other Lisp-y interpreters, but those will get blog posts and project pages of their own…)

:: Comments (3)

Arc!

Who cares about the Florida primaries when there is some *real* news? Arc is out. (Took them long enough. :-) The official site is here. I will probably download it and give it a spin tonight, after wrestling through the tutorial, forum discussions, and comment threads on Reddit and Y Combinator.

More about this later, without a doubt.

(First thought: It’s built on top of MzScheme. Can we do s/MzScheme/Chicken? :-)

:: Comments (1)

I can’t think of a title for this post. Anyway, it’s about Lisp. :)

Lisp: The Golden Age Isn’t Coming Back, Let’s Welcome a Bright Future: Interesting article that is somewhat relevant to me right now, considering what I’m doing (i.e. studying Chicken Scheme).

So, yeah, there really isn’t such a thing as “one Lisp”. There is “Lisp the concept”, and there are several standards (Common Lisp and Scheme being the most widely used), and many implementations, varying from very mature to experimental. This situation causes problems that other languages usually don’t have, like fragmentation, incompatible code bases, and lack to clarity due to too many choices. (Compare this to e.g. Python: you just download the latest version at www.python.org, install it, and you’re good to go. But you cannot go to, say, www.lisp.com, and download “the Lisp that everybody uses”. There are just too many variants to choose from.)

In fact, you could say that Lisp isn’t really a language, but a way to implement programming language concepts. You could write an implementation, interpreted or compiled or both, using separate namespaces for functions and variables or one, using dynamic or static typing, using eager or lazy evaluation, etc, and whatever you come up with could still be called “Lisp”. Scheme is “a Lisp”. So are Goo and Arc. The terminology can be confusing sometimes because people often use the term “Lisp” to refer to Common Lisp (sometimes in contrast to other Lisp variants like Scheme).

What the article kind of hints at, but doesn’t really go into, is that Lisp has social problems. If that sounds weird, just take a look at comp.lang.lisp to see what I mean. Or, if you just want the summary, see this blog. Also see e.g. here, here and here. This seems to be mostly true for the Common Lisp community; on average, Schemers seem to have a different attitude, and I found people on the chicken-users mailing list to be friendly and helpful in general.

:: Comments off

Why not CL?

Paul Graham on Lisp machines:

What made me switch was that Lisp machines (both Symbolics and LMI) were so gratuitously, baroquely complex. The manuals filled a whole shelf. Each component of the software was written as if it had to have every possible feature. The hackers who wrote it were the smartest and most energetic around. But there was no Steve Jobs to tell them “No, this is too complex.” So the guy in charge of writing the pretty-printer, for example, would decide. “This is going to be the most powerful pretty-printer ever written. It’s going to be able to do everything!”

Unfortunately this complexity persists in Common Lisp, which was pretty much copied directly from ZetaLisp. In fact, both of the worst flaws in CL are due to its origins on Lisp machines: both its complexity and the way it’s cut off from the OS.

Indeed, this is one of the reasons why Common Lisp is much less appealing to me than Scheme. I mean, I love the idea of a Lisp machine. I loved reading the old Zetalisp manuals (long after they were relevant — I wasn’t around back then). But I don’t love Common Lisp, for several reasons, and one of them is that has accumulated too many features. (There are other reasons, but that’s something for a separate post. So are my reasons for choosing Scheme, and choosing Chicken specifically.)

Having many features doesn’t make something a bad language, but it’s the “everything and the kitchen sink” mentality that turns me off. It reminds me of languages like Perl, or Ada. :-/ It’s just not something I am looking for right now.

(It’s an odd comment coming from PG though, considering he has written books about Common Lisp… but maybe this is one of the reasons he decided to start writing Arc.)

:: Comments off